use crate::gf28::GF28;
pub struct ReedSolomon<'a> {
gf: &'a GF28,
}
impl<'a> ReedSolomon<'a> {
pub fn new(gf: &'a GF28) -> Self {
Self { gf }
}
pub fn generator_polynomial(&self, ec_count: usize) -> Vec<u8> {
let mut poly = vec![1u8];
for i in 0..ec_count {
let alpha_i = self.gf.exp(i);
let mut next_poly = vec![0u8; poly.len() + 1];
for (j, &coeff) in poly.iter().enumerate() {
next_poly[j] ^= self.gf.mul(coeff, 1);
next_poly[j+1] ^= self.gf.mul(coeff, alpha_i);
}
poly = next_poly
}
poly
}
pub fn generate_ec_bytes(&self, data: &[u8], ec_count: usize) -> Vec<u8> {
let poly = self.generator_polynomial(ec_count);
let mut ec_buf = vec![0u8; ec_count];
for &byte in data {
let feedback = ec_buf[0] ^ byte;
ec_buf.copy_within(1.., 0);
ec_buf[ec_count-1] = 0;
if feedback != 0 {
for i in 0..ec_count {
ec_buf[i] ^= self.gf.mul(poly[i+1], feedback);
}
}
}
ec_buf
}
}
pub struct BlockSpec {
pub total_data_codewords: usize,
pub ec_codewords_per_block: usize,
pub g1_blocks: usize,
pub g1_data_per_block: usize,
pub g2_blocks: usize,
pub g2_data_per_block: usize,
}
pub fn interleave_blocks(data: &[u8], block_spec: &BlockSpec, rs: &ReedSolomon) -> Vec<u8> {
let mut data_blocks: Vec<Vec<u8>> = Vec::new();
let mut ec_blocks: Vec<Vec<u8>> = Vec::new();
let mut cursor = 0;
for _ in 0..block_spec.g1_blocks {
let end = cursor + block_spec.g1_data_per_block;
let block_data = data[cursor..end].to_vec();
let ec_data = rs.generate_ec_bytes(&block_data, block_spec.ec_codewords_per_block);
data_blocks.push(block_data);
ec_blocks.push(ec_data);
cursor = end;
}
for _ in 0..block_spec.g2_blocks {
let end = cursor + block_spec.g2_data_per_block;
let block_data = data[cursor..end].to_vec();
let ec_data = rs.generate_ec_bytes(&block_data, block_spec.ec_codewords_per_block);
data_blocks.push(block_data);
ec_blocks.push(ec_data);
cursor = end;
}
let mut result = Vec::with_capacity(data.len() + ec_blocks.len() * block_spec.ec_codewords_per_block);
let max_data_len = block_spec.g1_data_per_block.max(block_spec.g2_data_per_block);
for i in 0..max_data_len {
for block in &data_blocks {
if i < block.len() {
result.push(block[i]);
}
}
}
for i in 0..block_spec.ec_codewords_per_block {
for block in &ec_blocks {
result.push(block[i]);
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_reed_solomon() {
let gf = GF28::new();
let rs = ReedSolomon::new(&gf);
let data = [32, 91, 11, 120, 209, 114, 220, 77, 67, 64, 236, 17, 236, 17, 236, 17];
let expected = [196, 35, 39, 119, 235, 215, 231, 226, 93, 23];
let ec_bytes = rs.generate_ec_bytes(&data, 10);
assert_eq!(ec_bytes, expected);
}
#[test]
fn test_interleave_v1() {
let gf = GF28::new();
let rs = ReedSolomon::new(&gf);
let data = [32, 91, 11, 120, 209, 114, 220, 77, 67, 64, 236, 17, 236, 17, 236, 17];
let spec = BlockSpec {
total_data_codewords: 16,
ec_codewords_per_block: 10,
g1_blocks: 1,
g1_data_per_block: 16,
g2_blocks: 0,
g2_data_per_block: 0,
};
let interleaved = interleave_blocks(&data, &spec, &rs);
assert_eq!(interleaved.len(), 26);
assert_eq!(&interleaved[16..], &[196, 35, 39, 119, 235, 215, 231, 226, 93, 23]);
}
#[test]
fn test_interleave_v5_q() {
let gf = GF28::new();
let rs = ReedSolomon::new(&gf);
let data: Vec<u8> = (0..62).collect();
let spec = BlockSpec {
total_data_codewords: 62,
ec_codewords_per_block: 18,
g1_blocks: 2,
g1_data_per_block: 16,
g2_blocks: 2,
g2_data_per_block: 15,
};
let result = interleave_blocks(&data, &spec, &rs);
assert_eq!(result.len(), 134);
assert_eq!(result[0], 0);
assert_eq!(result[1], 16);
assert_eq!(result[2], 32);
assert_eq!(result[3], 47);
assert_eq!(result[4], 1);
assert_eq!(result[5], 17);
assert_eq!(result[60], 15); assert_eq!(result[61], 31);
let ec1 = rs.generate_ec_bytes(&data[0..16], 18);
let ec2 = rs.generate_ec_bytes(&data[16..32], 18);
let ec3 = rs.generate_ec_bytes(&data[32..47], 18);
let ec4 = rs.generate_ec_bytes(&data[47..62], 18);
assert_eq!(result[62], ec1[0]);
assert_eq!(result[63], ec2[0]);
assert_eq!(result[64], ec3[0]);
assert_eq!(result[65], ec4[0]);
assert_eq!(result[66], ec1[1]);
assert_eq!(result[67], ec2[1]);
assert_eq!(result[68], ec3[1]);
assert_eq!(result[69], ec4[1]);
}
}