const PRIMITIVE: u16 = 0x12D;
const GENERATOR_BASE: usize = 1;
struct Tables {
exp: [u8; 255],
log: [u8; 256],
}
const fn build_tables() -> Tables {
let mut exp = [0u8; 255];
let mut log = [0u8; 256];
let mut x: u16 = 1;
let mut i = 0;
while i < 255 {
exp[i] = x as u8;
log[x as usize] = i as u8;
x <<= 1;
if x & 0x100 != 0 {
x ^= PRIMITIVE;
}
i += 1;
}
Tables { exp, log }
}
const TABLES: Tables = build_tables();
pub fn mul(a: u8, b: u8) -> u8 {
if a == 0 || b == 0 {
return 0;
}
let idx = (TABLES.log[a as usize] as usize + TABLES.log[b as usize] as usize) % 255;
TABLES.exp[idx]
}
pub fn div(a: u8, b: u8) -> u8 {
assert!(b != 0, "division by zero in GF(256)");
if a == 0 {
return 0;
}
let idx = (TABLES.log[a as usize] as usize + 255 - TABLES.log[b as usize] as usize) % 255;
TABLES.exp[idx]
}
pub fn exp(n: usize) -> u8 {
TABLES.exp[n % 255]
}
pub fn inv(a: u8) -> u8 {
div(1, a)
}
fn poly_mul(a: &[u8], b: &[u8]) -> Vec<u8> {
let mut out = vec![0u8; a.len() + b.len() - 1];
for (i, &av) in a.iter().enumerate() {
for (j, &bv) in b.iter().enumerate() {
out[i + j] ^= mul(av, bv);
}
}
out
}
pub fn generator(ec_len: usize) -> Vec<u8> {
let mut g = vec![1u8];
for i in 0..ec_len {
g = poly_mul(&g, &[1, exp(GENERATOR_BASE + i)]);
}
g
}
pub fn encode(data: &[u8], ec_len: usize) -> Vec<u8> {
let gen_poly = generator(ec_len);
let mut rem = vec![0u8; ec_len];
for &d in data {
let factor = d ^ rem[0];
rem.remove(0);
rem.push(0);
if factor != 0 {
for (i, &g) in gen_poly.iter().enumerate().skip(1) {
rem[i - 1] ^= mul(g, factor);
}
}
}
rem
}
pub fn decode(received: &[u8], ec_len: usize) -> Option<Vec<u8>> {
let n = received.len();
let mut syndromes = vec![0u8; ec_len];
let mut all_zero = true;
for (i, syn) in syndromes.iter_mut().enumerate() {
let x = exp(GENERATOR_BASE + i);
let mut s = 0u8;
for &c in received {
s = mul(s, x) ^ c;
}
*syn = s;
if s != 0 {
all_zero = false;
}
}
if all_zero {
return Some(received.to_vec());
}
let mut sigma = vec![1u8];
let mut prev = vec![1u8];
let mut l = 0usize;
let mut m = 1usize;
let mut b = 1u8;
for n_iter in 0..ec_len {
let mut delta = syndromes[n_iter];
for i in 1..=l {
if i < sigma.len() {
delta ^= mul(sigma[i], syndromes[n_iter - i]);
}
}
if delta == 0 {
m += 1;
} else if 2 * l <= n_iter {
let t = sigma.clone();
sub_shift(&mut sigma, &prev, div(delta, b), m);
l = n_iter + 1 - l;
prev = t;
b = delta;
m = 1;
} else {
sub_shift(&mut sigma, &prev, div(delta, b), m);
m += 1;
}
}
while sigma.len() > 1 && *sigma.last().unwrap() == 0 {
sigma.pop();
}
let num_errors = sigma.len() - 1;
if num_errors > ec_len / 2 || num_errors == 0 {
return None;
}
let mut error_positions = Vec::new();
for j in 0..n {
let x_inv = exp((255 - (j % 255)) % 255);
let mut val = 0u8;
let mut pw = 1u8;
for &coef in &sigma {
val ^= mul(coef, pw);
pw = mul(pw, x_inv);
}
if val == 0 {
error_positions.push(n - 1 - j);
}
}
if error_positions.len() != num_errors {
return None;
}
let mut omega = vec![0u8; ec_len];
for (i, &s) in syndromes.iter().enumerate() {
for (k, &sg) in sigma.iter().enumerate() {
if i + k < ec_len {
omega[i + k] ^= mul(s, sg);
}
}
}
let mut corrected = received.to_vec();
for &pos in &error_positions {
let p = n - 1 - pos; let x = exp(p % 255); let x_inv = inv(x); let mut omega_val = 0u8;
let mut pw = 1u8;
for &c in &omega {
omega_val ^= mul(c, pw);
pw = mul(pw, x_inv);
}
let mut sigma_deriv = 0u8;
let mut pw = 1u8;
for (k, &c) in sigma.iter().enumerate() {
if k % 2 == 1 {
sigma_deriv ^= mul(c, pw);
pw = mul(pw, mul(x_inv, x_inv));
}
}
if sigma_deriv == 0 {
return None;
}
corrected[pos] ^= div(omega_val, sigma_deriv);
}
for i in 0..ec_len {
let mut s = 0u8;
let x = exp(GENERATOR_BASE + i);
for &c in &corrected {
s = mul(s, x) ^ c;
}
if s != 0 {
return None;
}
}
Some(corrected)
}
fn sub_shift(dst: &mut Vec<u8>, src: &[u8], scale: u8, shift: usize) {
let needed = src.len() + shift;
if dst.len() < needed {
dst.resize(needed, 0);
}
for (i, &s) in src.iter().enumerate() {
dst[i + shift] ^= mul(scale, s);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn field_axioms() {
assert_eq!(mul(0, 5), 0);
assert_eq!(mul(1, 5), 5);
for a in 1u8..=255 {
assert_eq!(mul(a, inv(a)), 1, "inverse of {a}");
}
assert_eq!(exp(255), 1);
}
#[test]
fn iso_16022_reference_ecc() {
let data = [142u8, 164, 186];
let ec = encode(&data, 5);
assert_eq!(ec, vec![114, 25, 5, 88, 102]);
}
#[test]
fn rs_roundtrip_clean() {
let data = [142u8, 164, 186];
let ec = encode(&data, 5);
let mut block = data.to_vec();
block.extend_from_slice(&ec);
assert_eq!(decode(&block, 5).as_deref(), Some(block.as_slice()));
}
#[test]
fn rs_corrects_errors() {
let data = [142u8, 164, 186];
let ec = encode(&data, 5);
let mut block = data.to_vec();
block.extend_from_slice(&ec);
let clean = block.clone();
block[0] ^= 0xFF;
block[4] ^= 0x3C;
assert_eq!(decode(&block, 5).as_deref(), Some(clean.as_slice()));
}
}