use std::sync::OnceLock;
const RS_POLY: u8 = 0x1D;
fn mul2_table() -> &'static [u8; 256] {
static TABLE: OnceLock<[u8; 256]> = OnceLock::new();
TABLE.get_or_init(|| {
let mut t = [0u8; 256];
for (i, slot) in t.iter_mut().enumerate() {
let x = u8::try_from(i).expect("table index < 256");
let shifted = x << 1;
*slot = if x & 0x80 != 0 {
shifted ^ RS_POLY
} else {
shifted
};
}
t
})
}
#[inline]
fn mul2(x: u8) -> u8 {
mul2_table()[x as usize]
}
#[must_use]
pub fn compute_p(data_stripes: &[&[u8]]) -> Vec<u8> {
let Some(first) = data_stripes.first() else {
return Vec::new();
};
let len = first.len();
debug_assert!(
data_stripes.iter().all(|s| s.len() == len),
"compute_p: stripes must have equal length"
);
let mut p = first.to_vec();
for stripe in &data_stripes[1..] {
for (out, &b) in p.iter_mut().zip(stripe.iter()) {
*out ^= b;
}
}
p
}
#[must_use]
pub fn compute_p_q(data_stripes: &[&[u8]]) -> (Vec<u8>, Vec<u8>) {
let Some(first) = data_stripes.first() else {
return (Vec::new(), Vec::new());
};
let len = first.len();
debug_assert!(
data_stripes.iter().all(|s| s.len() == len),
"compute_p_q: stripes must have equal length"
);
let mut p = vec![0u8; len];
let mut q = vec![0u8; len];
for stripe in data_stripes {
for i in 0..len {
p[i] ^= stripe[i];
q[i] = mul2(q[i]) ^ stripe[i];
}
}
(p, q)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mul2_table_basic_values() {
let t = mul2_table();
assert_eq!(t[0x00], 0x00);
assert_eq!(t[0x01], 0x02);
assert_eq!(t[0x40], 0x80);
assert_eq!(t[0x80], 0x1D);
assert_eq!(t[0x81], 0x1F);
assert_eq!(t[0xFF], 0xE3);
}
#[test]
fn mul2_doubling_is_associative_to_pow2() {
let mut x = 1u8;
for _ in 0..4 {
x = mul2(x);
}
assert_eq!(x, 16);
}
#[test]
fn compute_p_empty_input() {
let p = compute_p(&[]);
assert!(p.is_empty());
}
#[test]
fn compute_p_single_stripe_is_copy() {
let s = [0x11u8, 0x22, 0x33, 0x44];
let p = compute_p(&[&s]);
assert_eq!(p, s);
}
#[test]
fn compute_p_xor_of_known_pattern() {
let a = [0xFFu8, 0x00, 0xAA, 0x55];
let b = [0x0Fu8, 0xF0, 0x55, 0xAA];
let c = [0x33u8, 0x33, 0x33, 0x33];
let p = compute_p(&[&a, &b, &c]);
assert_eq!(p, vec![0xC3, 0xC3, 0xCC, 0xCC]);
}
#[test]
fn compute_p_xor_of_self_is_zero() {
let s = [0xDEu8, 0xAD, 0xBE, 0xEF];
let p = compute_p(&[&s, &s]);
assert_eq!(p, vec![0; 4]);
}
#[test]
fn compute_p_q_empty_input() {
let (p, q) = compute_p_q(&[]);
assert!(p.is_empty() && q.is_empty());
}
#[test]
fn compute_p_q_zero_data_is_zero() {
let z = [0u8; 8];
let (p, q) = compute_p_q(&[&z, &z, &z]);
assert_eq!(p, vec![0; 8]);
assert_eq!(q, vec![0; 8]);
}
#[test]
fn compute_p_q_single_stripe_p_is_copy_q_equals_stripe() {
let s = [1u8, 2, 3, 4, 0xFF];
let (p, q) = compute_p_q(&[&s]);
assert_eq!(p, s);
assert_eq!(q, s);
}
#[test]
fn compute_p_q_two_stripes_q_is_2a_xor_b() {
let a = [0x01u8, 0x02, 0x40, 0x80];
let b = [0xFFu8, 0x00, 0xAA, 0x55];
let (p, q) = compute_p_q(&[&a, &b]);
let expect_p: Vec<u8> =
a.iter().zip(b.iter()).map(|(x, y)| x ^ y).collect();
let expect_q: Vec<u8> =
a.iter().zip(b.iter()).map(|(x, y)| mul2(*x) ^ y).collect();
assert_eq!(p, expect_p);
assert_eq!(q, expect_q);
}
#[test]
fn compute_p_q_three_stripes_explicit() {
let s_a = [0x01u8];
let s_b = [0x40u8];
let s_c = [0x80u8];
let (p, q) = compute_p_q(&[&s_a, &s_b, &s_c]);
assert_eq!(p, vec![0x01 ^ 0x40 ^ 0x80]);
let expected_q = mul2(mul2(0x01) ^ 0x40) ^ 0x80;
assert_eq!(q, vec![expected_q]);
}
#[test]
fn compute_p_q_p_consistent_with_compute_p() {
let mut a = [0u8; 64];
let mut b = [0u8; 64];
let mut c = [0u8; 64];
for i in 0..64 {
a[i] = (i as u8).wrapping_mul(7);
b[i] = (i as u8).wrapping_mul(11);
c[i] = (i as u8).wrapping_mul(13);
}
let p_solo = compute_p(&[&a, &b, &c]);
let (p_dual, _q) = compute_p_q(&[&a, &b, &c]);
assert_eq!(p_solo, p_dual);
}
#[test]
fn compute_p_reconstruct_missing_stripe_via_xor() {
let a = [0x12u8, 0x34, 0x56, 0x78];
let b = [0x9Au8, 0xBC, 0xDE, 0xF0];
let c = [0x11u8, 0x22, 0x33, 0x44];
let p = compute_p(&[&a, &b, &c]);
let recon: Vec<u8> = p
.iter()
.zip(a.iter())
.zip(c.iter())
.map(|((p, x), y)| p ^ x ^ y)
.collect();
assert_eq!(recon, b);
}
}