Skip to main content

rlnc_simdx/
recoder.rs

1//! RLNC Recoder — re-encodes already-coded packets into new coded packets.
2//!
3//! All output buffers are [`AlignedBuffer`]-backed, ensuring the recoded
4//! payload passes 64-byte aligned pointers to the SIMD kernels.
5
6#[cfg(feature = "alloc")]
7extern crate alloc;
8
9use crate::aligned::AlignedBuffer;
10use crate::encoder::{CodedPacket, SimpleRng};
11use crate::error::RlncError;
12use crate::kernel;
13
14/// RLNC Recoder.
15pub struct Recoder;
16
17impl Recoder {
18    /// Produce a new coded packet from a set of already-coded packets.
19    ///
20    /// All input packets must have the same coefficient length and payload length.
21    #[cfg(feature = "alloc")]
22    pub fn recode(coded: &[CodedPacket], rng: &mut SimpleRng) -> Result<CodedPacket, RlncError> {
23        if coded.is_empty() {
24            return Err(RlncError::InvalidParameters);
25        }
26
27        let k = coded[0].coefficients.len();
28        let n = coded[0].payload.len();
29
30        for pkt in coded {
31            if pkt.coefficients.len() != k || pkt.payload.len() != n {
32                return Err(RlncError::PacketSizeMismatch {
33                    expected_coeffs: k,
34                    got_coeffs: pkt.coefficients.len(),
35                    expected_payload: n,
36                    got_payload: pkt.payload.len(),
37                });
38            }
39        }
40
41        let mut out_coeffs = AlignedBuffer::zeroed(k);
42        let mut out_payload = AlignedBuffer::zeroed(n);
43
44        let mut recode_coeffs = alloc::vec![0u8; coded.len()];
45        rng.fill(&mut recode_coeffs);
46        // Bounded retries — M4
47        let mut retries = 0u32;
48        while recode_coeffs.iter().all(|&c| c == 0) {
49            retries += 1;
50            if retries >= 100 {
51                recode_coeffs[0] = 1;
52                break;
53            }
54            rng.fill(&mut recode_coeffs);
55        }
56
57        for (i, &rc) in recode_coeffs.iter().enumerate() {
58            if rc == 0 {
59                continue;
60            }
61            kernel::axpy(
62                rc,
63                coded[i].coefficients.as_slice(),
64                out_coeffs.as_mut_slice(),
65            );
66            kernel::axpy(rc, coded[i].payload.as_slice(), out_payload.as_mut_slice());
67        }
68
69        Ok(CodedPacket {
70            coefficients: out_coeffs,
71            payload: out_payload,
72        })
73    }
74}
75
76#[cfg(test)]
77#[cfg(feature = "alloc")]
78mod tests {
79    use super::*;
80    use crate::decoder::Decoder;
81    use crate::encoder::Encoder;
82
83    #[test]
84    fn recode_then_decode() {
85        let k = 4usize;
86        let n = 32usize;
87        let source: alloc::vec::Vec<alloc::vec::Vec<u8>> = (0..k)
88            .map(|i| alloc::vec![(i as u8).wrapping_mul(17); n])
89            .collect();
90        let refs: alloc::vec::Vec<&[u8]> = source.iter().map(Vec::as_slice).collect();
91
92        let enc = Encoder::new(k, n).unwrap();
93        let mut rng = SimpleRng::new(0x1234_5678);
94
95        // Full-rank source set: mix systematic + random for reliable span
96        let mut coded: alloc::vec::Vec<CodedPacket> = (0..k)
97            .map(|i| enc.encode_systematic(&refs, i).unwrap())
98            .collect();
99        for _ in 0..k {
100            coded.push(enc.encode_random(&refs, &mut rng).unwrap());
101        }
102
103        let mut dec = Decoder::new(k, n).unwrap();
104        let mut recode_rng = SimpleRng::new(0xABCD_EF01);
105        // Enough recodes to finish with high probability; fail hard if not
106        for _ in 0..k * 4 {
107            let recoded = Recoder::recode(&coded, &mut recode_rng).unwrap();
108            let _ = dec.receive(recoded);
109            if dec.is_complete() {
110                break;
111            }
112        }
113
114        assert!(
115            dec.is_complete(),
116            "recode stream never reached full rank (rank={})",
117            dec.rank()
118        );
119        let decoded = dec.decode().unwrap().expect("decode after complete");
120        assert_eq!(decoded.len(), k);
121        for i in 0..k {
122            assert_eq!(decoded[i], source[i], "symbol {i} mismatch after recode");
123        }
124    }
125
126    #[test]
127    fn recode_empty_is_invalid() {
128        let mut rng = SimpleRng::new(1);
129        let err = Recoder::recode(&[], &mut rng).unwrap_err();
130        assert_eq!(err, RlncError::InvalidParameters);
131    }
132
133    #[test]
134    fn recode_size_mismatch() {
135        let mut rng = SimpleRng::new(2);
136        let a = CodedPacket::from_slices(&[1, 0], &[9, 9]);
137        let b = CodedPacket::from_slices(&[0, 1, 0], &[8, 8]); // wrong coeff len
138        let err = Recoder::recode(&[a, b], &mut rng).unwrap_err();
139        match err {
140            RlncError::PacketSizeMismatch { .. } => {}
141            other => panic!("expected PacketSizeMismatch, got {other:?}"),
142        }
143    }
144}