use crate::{long, ReedSolomon};
#[must_use]
pub fn validate(received: &[u8], parity: u8) -> bool {
if let Ok(length) = u8::try_from(received.len()) {
if parity > length >> 1 {
return false;
}
let Ok(rs) = ReedSolomon::new(parity) else {
return false;
};
rs.validate(received).is_none()
} else {
matches!(long::fast_validate(received), Ok((_, true)))
}
}
#[cfg(test)]
mod tests {
use crate::{
long, validate, LongEccDecodeError, LongEccEncodeError, RSEncodeError, ReedSolomon,
};
use ps_buffer::ToBuffer;
#[derive(thiserror::Error, Debug)]
enum TestError {
#[error(transparent)]
LongEccEncode(#[from] LongEccEncodeError),
#[error(transparent)]
LongEccDecode(#[from] LongEccDecodeError),
#[error(transparent)]
Buffer(#[from] ps_buffer::BufferError),
#[error(transparent)]
RSConstructorError(#[from] crate::RSConstructorError),
#[error(transparent)]
RSEncodeError(#[from] RSEncodeError),
}
#[test]
fn test_validate_short_data_valid_no_errors() -> Result<(), TestError> {
let data = b"test";
let parity = 2;
let rs = ReedSolomon::new(parity)?;
let codeword = rs.encode(data)?;
assert!(validate(&codeword, parity));
Ok(())
}
#[test]
fn test_validate_short_data_invalid_with_errors() -> Result<(), TestError> {
let data = b"test";
let parity = 2;
let rs = ReedSolomon::new(parity)?;
let mut codeword = rs.encode(data)?;
codeword[0] ^= 1;
codeword[1] ^= 1;
codeword[2] ^= 1;
assert!(!validate(&codeword, parity));
Ok(())
}
#[test]
fn test_validate_short_data_parity_too_large() {
let data = b"test"; let parity = 3;
assert!(!validate(data, parity));
}
#[test]
fn test_validate_short_data_rs_constructor_error() {
let data = b"test";
let parity = 255;
assert!(!validate(data, parity));
}
#[test]
fn test_validate_short_data_correctable_errors() -> Result<(), TestError> {
let data = b"test";
let parity = 2;
let rs = ReedSolomon::new(parity)?;
let mut codeword = rs.encode(data)?;
codeword[0] ^= 1;
assert!(!validate(&codeword, parity));
Ok(())
}
#[test]
fn test_validate_long_data_valid_no_errors() -> Result<(), TestError> {
let message = b"This is a longer message that will use long ECC".repeat(7);
let parity = 2;
let encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
assert!(validate(&encoded, parity));
Ok(())
}
#[test]
fn test_validate_long_data_invalid_with_errors() -> Result<(), TestError> {
let message = b"This is a longer message that will use long ECC".to_buffer()?;
let parity = 2;
let mut encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
encoded[32] ^= 1;
encoded[37] ^= 1;
assert!(!validate(&encoded, parity));
Ok(())
}
#[test]
fn test_validate_long_data_fast_path_valid() -> Result<(), TestError> {
let message = b"Fast path validation test".repeat(12);
let parity = 1;
let encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
assert!(validate(&encoded, parity));
Ok(())
}
#[test]
fn test_validate_empty_data() {
let data = b"";
let parity = 0;
assert!(validate(data, parity));
}
#[test]
fn test_validate_single_byte() -> Result<(), TestError> {
let data = b"A";
let parity = 1;
let rs = ReedSolomon::new(parity)?;
let codeword = rs.encode(data)?;
assert!(validate(&codeword, parity));
Ok(())
}
#[test]
fn test_validate_large_short_data() -> Result<(), TestError> {
let data = b"This is exactly 32 bytes of test data!!";
let parity = 4;
let rs = ReedSolomon::new(parity)?;
let codeword = rs.encode(data)?;
assert!(validate(&codeword, parity));
Ok(())
}
#[test]
fn test_validate_edge_case_parity_equals_length_div_2() -> Result<(), TestError> {
let data = b"test"; let parity = 2;
let rs = ReedSolomon::new(parity)?;
let codeword = rs.encode(data)?;
assert!(validate(&codeword, parity));
Ok(())
}
#[test]
fn test_validate_edge_case_parity_just_over_length_div_2() {
let data = b"test"; let parity = 3;
assert!(!validate(data, parity));
}
#[test]
fn test_validate_long_data_with_zero_parity() -> Result<(), TestError> {
let message = b"Zero parity test".to_buffer()?;
let parity = 0;
let encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
assert!(validate(&encoded, parity));
Ok(())
}
#[test]
fn test_validate_long_data_header_corrupted() -> Result<(), TestError> {
let message = b"Header corruption test".to_buffer()?;
let parity = 2;
let mut encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
encoded[0] ^= 1;
encoded[5] ^= 1;
assert!(!validate(&encoded, parity));
Ok(())
}
#[test]
fn test_validate_short_data_length_conversion_error() {
let data: Vec<u8> = vec![0x42; 300]; let parity = 2;
assert!(!validate(&data, parity));
}
#[test]
fn test_validate_short_data_unrecoverable_errors() -> Result<(), TestError> {
let data = b"test data";
let parity = 1;
let rs = ReedSolomon::new(parity)?;
let mut codeword = rs.encode(data)?;
codeword[0] ^= 1;
codeword[1] ^= 1;
assert!(!validate(&codeword, parity));
Ok(())
}
#[test]
fn test_validate_long_data_corrupted_parity() -> Result<(), TestError> {
let message = b"Corrupted parity test".repeat(13).to_buffer()?;
let parity = 2;
let mut encoded = long::encode(&message, parity, long::OverlapFactor::Simple)?;
let parity_start = 32 + message.len();
if parity_start < encoded.len() {
encoded[parity_start] ^= 1;
}
assert!(!validate(&encoded, parity));
Ok(())
}
}