use crate::stego::armor::ecc::{
rs_decode_blocks_with_parity, rs_encode_blocks_with_parity,
rs_encoded_len_with_parity, RsDecodeError, RsDecodeStats,
};
pub const PRIMARY_PARITY_TIERS: [usize; 7] = [0, 4, 8, 16, 32, 64, 128];
pub fn primary_rs_encode(frame_bytes: &[u8], parity_len: usize) -> Vec<u8> {
if parity_len == 0 {
return frame_bytes.to_vec();
}
rs_encode_blocks_with_parity(frame_bytes, parity_len)
}
pub fn primary_rs_decode(
received: &[u8],
data_len: usize,
parity_len: usize,
) -> Result<(Vec<u8>, RsDecodeStats), RsDecodeError> {
if parity_len == 0 {
if received.len() != data_len {
return Err(RsDecodeError);
}
return Ok((received.to_vec(), RsDecodeStats::default()));
}
rs_decode_blocks_with_parity(received, data_len, parity_len)
}
pub fn primary_rs_encoded_len(data_len: usize, parity_len: usize) -> usize {
if parity_len == 0 {
return data_len;
}
rs_encoded_len_with_parity(data_len, parity_len)
}
#[cfg(test)]
mod tests {
use super::*;
fn make_frame(len: usize) -> Vec<u8> {
(0..len).map(|i| ((i * 37 + 13) & 0xff) as u8).collect()
}
#[test]
fn round_trip_parity_zero_is_identity() {
let frame = make_frame(64);
let encoded = primary_rs_encode(&frame, 0);
assert_eq!(encoded, frame);
let (decoded, _) = primary_rs_decode(&encoded, frame.len(), 0).unwrap();
assert_eq!(decoded, frame);
}
#[test]
fn round_trip_at_each_parity_tier() {
let frame = make_frame(96);
for &parity in &PRIMARY_PARITY_TIERS {
let encoded = primary_rs_encode(&frame, parity);
assert_eq!(
encoded.len(),
primary_rs_encoded_len(frame.len(), parity),
"encoded len mismatch at parity {parity}"
);
let (decoded, stats) =
primary_rs_decode(&encoded, frame.len(), parity).unwrap();
assert_eq!(decoded, frame, "round-trip mismatch at parity {parity}");
assert_eq!(stats.total_errors, 0, "unexpected errors at parity {parity}");
}
}
#[test]
fn rs_absorbs_byte_errors_up_to_capacity() {
let frame = make_frame(80);
let parity = 16;
let mut encoded = primary_rs_encode(&frame, parity);
for &pos in &[0_usize, 7, 13, 25, 47, 71] {
encoded[pos] ^= 0xa5;
}
let (decoded, stats) = primary_rs_decode(&encoded, frame.len(), parity)
.expect("RS decode should succeed under capacity");
assert_eq!(decoded, frame);
assert_eq!(stats.total_errors, 6);
}
#[test]
fn rs_fails_when_errors_exceed_capacity() {
let frame = make_frame(80);
let parity = 8; let mut encoded = primary_rs_encode(&frame, parity);
for &pos in &[0_usize, 11, 22, 33, 44] {
encoded[pos] ^= 0xa5;
}
let result = primary_rs_decode(&encoded, frame.len(), parity);
assert!(result.is_err(), "expected RS decode failure over capacity");
}
#[test]
fn parity_zero_decode_rejects_length_mismatch() {
let frame = make_frame(64);
let mut encoded = primary_rs_encode(&frame, 0);
encoded.push(0); assert!(primary_rs_decode(&encoded, frame.len(), 0).is_err());
}
#[test]
fn rs_handles_multi_block_frames() {
let frame = make_frame(500);
let parity = 16;
let encoded = primary_rs_encode(&frame, parity);
assert_eq!(
encoded.len(),
primary_rs_encoded_len(frame.len(), parity)
);
let (decoded, stats) = primary_rs_decode(&encoded, frame.len(), parity).unwrap();
assert_eq!(decoded, frame);
assert_eq!(stats.num_blocks, 3);
}
}