leodos_protocols/coding/fec/
reed_solomon.rs1pub const N: usize = 255;
23pub const K: usize = 223;
25pub const PARITY: usize = N - K; pub const T: usize = PARITY / 2; const FCR: u8 = 112;
31
32mod gf {
34 const POLY: u16 = 0x187;
36
37 pub(super) static EXP: [u8; 512] = {
40 let mut t = [0u8; 512];
41 let mut val: u16 = 1;
42 let mut i = 0;
43 while i < 255 {
44 t[i] = val as u8;
45 t[i + 255] = val as u8;
46 val <<= 1;
48 if val & 0x100 != 0 {
49 val ^= POLY;
50 }
51 i += 1;
52 }
53 t[255] = t[0];
54 t
55 };
56
57 pub(super) static LOG: [u8; 256] = {
59 let mut t = [0u8; 256];
60 let mut i = 0u16;
61 while i < 255 {
62 t[EXP[i as usize] as usize] = i as u8;
63 i += 1;
64 }
65 t
66 };
67
68 #[inline]
70 pub fn exp(n: usize) -> u8 {
71 EXP[n % 255]
72 }
73
74 #[inline]
76 pub fn log(x: u8) -> u8 {
77 LOG[x as usize]
78 }
79
80 #[inline]
82 pub fn mul(a: u8, b: u8) -> u8 {
83 if a == 0 || b == 0 {
84 return 0;
85 }
86 exp(log(a) as usize + log(b) as usize)
87 }
88
89 #[inline]
91 pub fn add(a: u8, b: u8) -> u8 {
92 a ^ b
93 }
94
95 #[inline]
97 pub fn inv(a: u8) -> u8 {
98 if a == 0 {
99 return 0;
100 }
101 EXP[255 - LOG[a as usize] as usize]
102 }
103
104 #[inline]
106 pub fn div(a: u8, b: u8) -> u8 {
107 if a == 0 {
108 return 0;
109 }
110 mul(a, inv(b))
111 }
112
113 pub fn poly_eval(poly: &[u8], x: u8) -> u8 {
116 if poly.is_empty() {
117 return 0;
118 }
119 let mut result = poly[0];
120 for &coef in &poly[1..] {
121 result = add(mul(result, x), coef);
122 }
123 result
124 }
125
126 pub fn poly_eval_asc(poly: &[u8], x: u8) -> u8 {
129 if poly.is_empty() {
130 return 0;
131 }
132 let mut result = poly[poly.len() - 1];
133 for i in (0..poly.len() - 1).rev() {
134 result = add(mul(result, x), poly[i]);
135 }
136 result
137 }
138}
139
140#[derive(Debug, Copy, Clone, Eq, PartialEq)]
142pub enum RsError {
143 BufferTooShort {
145 required: usize,
147 provided: usize,
149 },
150 TooManyErrors,
152 InvalidInterleaveDepth(u8),
154}
155
156static GENERATOR: [u8; PARITY + 1] = {
161 let mut g = [0u8; PARITY + 1];
163 g[0] = 1;
164 let mut len = 1;
165
166 let mut i = 0u16;
167 while i < PARITY as u16 {
168 let root = gf::EXP[(FCR as u16 + i) as usize % 255];
170 let mut j = len;
171 while j > 0 {
172 if g[j - 1] != 0 {
173 g[j] = g[j] ^ gf::EXP
174 [((gf::LOG[g[j - 1] as usize] as u16
175 + gf::LOG[root as usize] as u16)
176 % 255) as usize];
177 }
178 j -= 1;
179 }
180 len += 1;
181 i += 1;
182 }
183 g
184};
185
186pub fn encode(data: &[u8], output: &mut [u8]) -> Result<usize, RsError> {
191 let data_len = data.len();
192 if data_len > K {
193 return Err(RsError::BufferTooShort {
194 required: K,
195 provided: data_len,
196 });
197 }
198 if output.len() < N {
199 return Err(RsError::BufferTooShort {
200 required: N,
201 provided: output.len(),
202 });
203 }
204
205 let mut codeword = [0u8; N];
206 codeword[..data_len].copy_from_slice(data);
207
208 let mut remainder = [0u8; PARITY];
211 for i in 0..K {
212 let feedback = gf::add(codeword[i], remainder[0]);
213 if feedback != 0 {
214 for j in 0..PARITY - 1 {
215 remainder[j] = gf::add(
216 remainder[j + 1],
217 gf::mul(feedback, GENERATOR[j + 1]),
218 );
219 }
220 remainder[PARITY - 1] =
221 gf::mul(feedback, GENERATOR[PARITY]);
222 } else {
223 remainder.copy_within(1..PARITY, 0);
224 remainder[PARITY - 1] = 0;
225 }
226 }
227
228 output[..K].copy_from_slice(&codeword[..K]);
229 output[K..N].copy_from_slice(&remainder);
230
231 Ok(N)
232}
233
234fn syndromes(codeword: &[u8; N]) -> [u8; PARITY] {
236 let mut s = [0u8; PARITY];
237 for j in 0..PARITY {
238 let root = gf::exp(FCR as usize + j);
239 s[j] = gf::poly_eval(codeword, root);
240 }
241 s
242}
243
244fn berlekamp_massey(
249 syn: &[u8; PARITY],
250) -> Result<([u8; PARITY + 1], usize), RsError> {
251 let mut sigma = [0u8; PARITY + 1];
252 sigma[0] = 1;
253 let mut old_sigma = [0u8; PARITY + 1];
254 old_sigma[0] = 1;
255 let mut l = 0usize;
256
257 for n in 0..PARITY {
258 let mut delta = syn[n];
260 for i in 1..=l {
261 delta = gf::add(delta, gf::mul(sigma[i], syn[n - i]));
262 }
263
264 let mut shifted = [0u8; PARITY + 1];
266 for i in 1..=PARITY {
267 shifted[i] = old_sigma[i - 1];
268 }
269 old_sigma = shifted;
270
271 if delta != 0 {
272 let mut scaled = [0u8; PARITY + 1];
273 for i in 0..=PARITY {
274 scaled[i] = gf::mul(delta, old_sigma[i]);
275 }
276
277 if 2 * l <= n {
279 let inv_delta = gf::inv(delta);
280 for i in 0..=PARITY {
281 old_sigma[i] =
282 gf::mul(sigma[i], inv_delta);
283 }
284 l = n + 1 - l;
285 }
286
287 for i in 0..=PARITY {
289 sigma[i] = gf::add(sigma[i], scaled[i]);
290 }
291 }
292 }
293
294 if l > T {
295 return Err(RsError::TooManyErrors);
296 }
297
298 Ok((sigma, l))
299}
300
301fn chien_search(
306 sigma: &[u8; PARITY + 1],
307 num_errors: usize,
308) -> Result<[u8; T], RsError> {
309 let mut positions = [0u8; T];
310 let mut found = 0;
311
312 for m in 0..N {
313 let x = gf::exp(m);
314 if gf::poly_eval_asc(&sigma[..=num_errors], x) == 0 {
315 positions[found] = ((m + N - 1) % N) as u8;
316 found += 1;
317 if found == num_errors {
318 break;
319 }
320 }
321 }
322
323 if found != num_errors {
324 return Err(RsError::TooManyErrors);
325 }
326
327 Ok(positions)
328}
329
330fn forney(
334 syn: &[u8; PARITY],
335 sigma: &[u8; PARITY + 1],
336 positions: &[u8],
337 num_errors: usize,
338) -> [u8; T] {
339 let mut omega = [0u8; PARITY];
341 for i in 0..PARITY {
342 let mut val = 0u8;
343 for j in 0..=i.min(num_errors) {
344 val = gf::add(val, gf::mul(sigma[j], syn[i - j]));
345 }
346 omega[i] = val;
347 }
348
349 let mut sigma_deriv = [0u8; PARITY];
351 for i in (1..=num_errors).step_by(2) {
352 sigma_deriv[i - 1] = sigma[i];
353 }
354
355 let mut magnitudes = [0u8; T];
356 for k in 0..num_errors {
357 let pos = positions[k] as usize;
358 let x_k_log = (N - 1 - pos) % N;
360 let x_k_inv = gf::exp((N - x_k_log) % N);
361
362 let omega_val =
363 gf::poly_eval_asc(&omega[..PARITY], x_k_inv);
364 let deriv_val =
365 gf::poly_eval_asc(&sigma_deriv[..num_errors], x_k_inv);
366
367 let power_log =
368 (x_k_log * (1 + N - FCR as usize)) % N;
369 let power = gf::exp(power_log);
370
371 magnitudes[k] =
372 gf::mul(power, gf::div(omega_val, deriv_val));
373 }
374
375 magnitudes
376}
377
378pub fn decode(codeword: &mut [u8; N]) -> Result<usize, RsError> {
383 let syn = syndromes(codeword);
384
385 if syn.iter().all(|&s| s == 0) {
386 return Ok(0);
387 }
388
389 let (sigma, num_errors) = berlekamp_massey(&syn)?;
390 let positions = chien_search(&sigma, num_errors)?;
391 let magnitudes = forney(&syn, &sigma, &positions, num_errors);
392
393 for i in 0..num_errors {
394 let pos = positions[i] as usize;
395 codeword[pos] = gf::add(codeword[pos], magnitudes[i]);
396 }
397
398 let check = syndromes(codeword);
400 if check.iter().all(|&s| s == 0) {
401 Ok(num_errors)
402 } else {
403 Err(RsError::TooManyErrors)
404 }
405}
406
407pub fn encode_interleaved(
412 data: &[u8],
413 depth: u8,
414 output: &mut [u8],
415) -> Result<usize, RsError> {
416 if depth == 0 || depth > 5 {
417 return Err(RsError::InvalidInterleaveDepth(depth));
418 }
419 let d = depth as usize;
420 let total_data = d * K;
421 let total_code = d * N;
422
423 if data.len() < total_data {
424 return Err(RsError::BufferTooShort {
425 required: total_data,
426 provided: data.len(),
427 });
428 }
429 if output.len() < total_code {
430 return Err(RsError::BufferTooShort {
431 required: total_code,
432 provided: output.len(),
433 });
434 }
435
436 for i in 0..d {
437 let mut block = [0u8; K];
439 for j in 0..K {
440 block[j] = data[j * d + i];
441 }
442
443 let mut codeword = [0u8; N];
444 encode(&block, &mut codeword)?;
445
446 for j in 0..N {
448 output[j * d + i] = codeword[j];
449 }
450 }
451
452 Ok(total_code)
453}
454
455pub fn decode_interleaved(
460 data: &mut [u8],
461 depth: u8,
462) -> Result<usize, RsError> {
463 if depth == 0 || depth > 5 {
464 return Err(RsError::InvalidInterleaveDepth(depth));
465 }
466 let d = depth as usize;
467 let total_code = d * N;
468
469 if data.len() < total_code {
470 return Err(RsError::BufferTooShort {
471 required: total_code,
472 provided: data.len(),
473 });
474 }
475
476 let mut total_corrected = 0;
477
478 for i in 0..d {
479 let mut codeword = [0u8; N];
481 for j in 0..N {
482 codeword[j] = data[j * d + i];
483 }
484
485 let corrected = decode(&mut codeword)?;
486 total_corrected += corrected;
487
488 for j in 0..N {
490 data[j * d + i] = codeword[j];
491 }
492 }
493
494 Ok(total_corrected)
495}
496
497pub struct ReedSolomonEncoder {
499 interleave_depth: u8,
500}
501
502impl ReedSolomonEncoder {
503 pub fn new(interleave_depth: u8) -> Self {
505 Self { interleave_depth }
506 }
507}
508
509impl crate::coding::FecEncoder for ReedSolomonEncoder {
510 type Error = RsError;
511
512 fn encode(&self, data: &[u8], output: &mut [u8]) -> Result<usize, Self::Error> {
513 if self.interleave_depth <= 1 {
514 encode(data, output)
515 } else {
516 encode_interleaved(data, self.interleave_depth, output)
517 }
518 }
519}
520
521pub struct ReedSolomonDecoder {
523 interleave_depth: u8,
524}
525
526impl ReedSolomonDecoder {
527 pub fn new(interleave_depth: u8) -> Self {
529 Self { interleave_depth }
530 }
531}
532
533impl crate::coding::FecDecoder for ReedSolomonDecoder {
534 type Error = RsError;
535
536 fn decode(&self, data: &mut [u8]) -> Result<usize, Self::Error> {
537 if self.interleave_depth <= 1 {
538 if data.len() < N {
539 return Err(RsError::BufferTooShort { required: N, provided: data.len() });
540 }
541 let codeword: &mut [u8; N] = (&mut data[..N]).try_into().unwrap();
542 decode(codeword)
543 } else {
544 decode_interleaved(data, self.interleave_depth)
545 }
546 }
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552
553 #[test]
554 fn gf_basic_arithmetic() {
555 assert_eq!(gf::exp(0), 1);
557 assert_eq!(gf::exp(1), 2);
559 assert_eq!(gf::exp(255), 1);
561 assert_eq!(gf::mul(42, 1), 42);
563 assert_eq!(gf::mul(42, 0), 0);
565 for a in 1..=255u8 {
567 assert_eq!(gf::mul(a, gf::inv(a)), 1);
568 }
569 }
570
571 #[test]
572 fn gf_log_exp_inverse() {
573 for i in 0..255usize {
574 let a = gf::exp(i);
575 assert_eq!(gf::log(a) as usize, i);
576 }
577 }
578
579 #[test]
580 fn encode_no_error_decode() {
581 let data = [0xAB; K];
582 let mut codeword = [0u8; N];
583 encode(&data, &mut codeword).unwrap();
584
585 assert_eq!(&codeword[..K], &data);
587
588 assert!(codeword[K..].iter().any(|&b| b != 0));
590
591 let corrected = decode(&mut codeword).unwrap();
593 assert_eq!(corrected, 0);
594 }
595
596 #[test]
597 fn encode_decode_single_error() {
598 let data = [0x42; K];
599 let mut codeword = [0u8; N];
600 encode(&data, &mut codeword).unwrap();
601
602 codeword[50] ^= 0xFF;
604
605 let corrected = decode(&mut codeword).unwrap();
606 assert_eq!(corrected, 1);
607 assert_eq!(&codeword[..K], &[0x42; K]);
608 }
609
610 #[test]
611 fn encode_decode_max_errors() {
612 let data = [0x13; K];
613 let mut codeword = [0u8; N];
614 encode(&data, &mut codeword).unwrap();
615 let original = codeword;
616
617 for i in 0..T {
619 codeword[i * 15] ^= ((i as u8) + 1).wrapping_mul(0x37);
620 }
621
622 let corrected = decode(&mut codeword).unwrap();
623 assert_eq!(corrected, T);
624 assert_eq!(codeword, original);
625 }
626
627 #[test]
628 fn too_many_errors() {
629 let data = [0x00; K];
630 let mut codeword = [0u8; N];
631 encode(&data, &mut codeword).unwrap();
632
633 for i in 0..=T {
635 codeword[i] ^= 0xFF;
636 }
637
638 let result = decode(&mut codeword);
639 assert!(result.is_err());
640 }
641
642 #[test]
643 fn generator_polynomial_degree() {
644 assert_eq!(GENERATOR[0], 1); assert!(GENERATOR[1..].iter().any(|&c| c != 0));
648 }
649
650 #[test]
651 fn generator_roots() {
652 for i in 0..PARITY {
654 let root = gf::exp(FCR as usize + i);
655 let val = gf::poly_eval(&GENERATOR, root);
656 assert_eq!(val, 0, "α^{} should be a root", FCR as usize + i);
657 }
658 }
659
660 #[test]
661 fn encode_zeros() {
662 let data = [0u8; K];
663 let mut codeword = [0u8; N];
664 encode(&data, &mut codeword).unwrap();
665 assert!(codeword.iter().all(|&b| b == 0));
667 }
668
669 #[test]
670 fn interleaved_encode_decode_depth_1() {
671 let data = [0x55; K];
672 let mut output = [0u8; N];
673 let len = encode_interleaved(&data, 1, &mut output).unwrap();
674 assert_eq!(len, N);
675
676 let corrected =
677 decode_interleaved(&mut output, 1).unwrap();
678 assert_eq!(corrected, 0);
679 assert_eq!(&output[..K], &data);
680 }
681
682 #[test]
683 fn interleaved_depth_2_with_errors() {
684 let data = [0xAA; 2 * K];
685 let mut output = [0u8; 2 * N];
686 encode_interleaved(&data, 2, &mut output).unwrap();
687
688 output[0] ^= 0x11; output[1] ^= 0x22; let corrected =
693 decode_interleaved(&mut output, 2).unwrap();
694 assert_eq!(corrected, 2);
695 }
696
697 #[test]
698 fn invalid_interleave_depth() {
699 let data = [0u8; K];
700 let mut output = [0u8; N];
701 assert!(matches!(
702 encode_interleaved(&data, 0, &mut output),
703 Err(RsError::InvalidInterleaveDepth(0))
704 ));
705 assert!(matches!(
706 encode_interleaved(&data, 6, &mut output),
707 Err(RsError::InvalidInterleaveDepth(6))
708 ));
709 }
710
711 #[test]
712 fn parity_error_correction() {
713 let data = [0x77; K];
714 let mut codeword = [0u8; N];
715 encode(&data, &mut codeword).unwrap();
716 let original = codeword;
717
718 codeword[K] ^= 0xFF;
720 codeword[K + 5] ^= 0xAA;
721
722 let corrected = decode(&mut codeword).unwrap();
723 assert_eq!(corrected, 2);
724 assert_eq!(codeword, original);
725 }
726
727 #[test]
728 fn end_to_end_example() {
729 let mut data = [0u8; K];
731 data[0] = 72; data[1] = 69; data[2] = 76; data[3] = 76; data[4] = 79; let mut codeword = [0u8; N];
739 encode(&data, &mut codeword).unwrap();
740
741 assert_eq!(&codeword[..5], &[72, 69, 76, 76, 79]);
743 assert_eq!(
745 &codeword[K..K + 8],
746 &[243, 147, 197, 58, 154, 156, 250, 218]
747 );
748
749 codeword[2] = 255;
751
752 let corrected = decode(&mut codeword).unwrap();
754 assert_eq!(corrected, 1);
755 assert_eq!(codeword[2], 76); }
757}