ps_ecc/
methods.rs

1use ps_buffer::Buffer;
2
3use crate::{codeword::Codeword, long, DecodeError, EncodeError, ReedSolomon};
4
5/// Encodes a message by adding an error-correcting code.
6/// # Errors
7/// - `RSConstructorError` is returned if `len(message) + 2 * parity` > `255`.
8/// - `RSEncodeError` is returned if encoding fails for any reason.
9pub fn encode(message: &[u8], parity: u8) -> Result<Buffer, EncodeError> {
10    if message.len() + (usize::from(parity) << 1) > 0xff {
11        let segment_length = 0xFF - (parity << 1);
12        let codeword = long::encode(message, parity, segment_length, segment_length)?;
13
14        return Ok(codeword);
15    }
16
17    let rs = ReedSolomon::new(parity)?;
18
19    Ok(rs.encode(message)?)
20}
21
22/// Verifies the error-correcting code and returns the message.
23/// # Errors
24/// - `InputTooLarge` is returned if `len(received)` > 255 bytes.
25/// - `InsufficientParityBytes` is returned if `parity > length / 2`.
26/// - `RSDecodeError` is returned if decoding fails for any reason.
27pub fn decode(received: &[u8], parity: u8) -> Result<Codeword, DecodeError> {
28    if let Ok(length) = u8::try_from(received.len()) {
29        if parity > length >> 1 {
30            return Err(DecodeError::InsufficientParityBytes(parity, length));
31        }
32
33        let rs = ReedSolomon::new(parity)?;
34
35        Ok(rs.decode(received)?)
36    } else {
37        Ok(long::decode(received)?)
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use crate::EccError;
44
45    use super::{decode, encode};
46
47    #[test]
48    fn ecc_works() -> Result<(), EccError> {
49        let test_str = "Strč prst skrz krk! ¯\\_(ツ)_/¯".as_bytes();
50        let mut encoded = encode(test_str, 13)?;
51
52        for i in 0..13 {
53            let index = (i * 37) % encoded.len();
54            encoded[index] ^= (i * index + 13).to_le_bytes()[0];
55            let decoded = decode(&encoded, 13)?;
56
57            assert_eq!(test_str, &decoded[..]);
58        }
59
60        Ok(())
61    }
62}