1#[cfg(feature = "alloc")]
8extern crate alloc;
9#[cfg(feature = "alloc")]
10use alloc::vec::Vec;
11
12use crate::aligned::AlignedBuffer;
13use crate::error::RlncError;
14use crate::kernel;
15
16#[cfg(feature = "alloc")]
18#[derive(Debug, Clone)]
19pub struct CodedPacket {
20 pub coefficients: AlignedBuffer,
23 pub payload: AlignedBuffer,
25}
26
27#[cfg(feature = "alloc")]
28impl CodedPacket {
29 #[inline]
31 pub fn coefficients_slice(&self) -> &[u8] {
32 self.coefficients.as_slice()
33 }
34
35 #[inline]
37 pub fn payload_slice(&self) -> &[u8] {
38 self.payload.as_slice()
39 }
40
41 pub fn into_vecs(self) -> (Vec<u8>, Vec<u8>) {
43 (self.coefficients.into_vec(), self.payload.into_vec())
44 }
45
46 pub fn from_slices(coefficients: &[u8], payload: &[u8]) -> Self {
48 Self {
49 coefficients: AlignedBuffer::from_slice(coefficients),
50 payload: AlignedBuffer::from_slice(payload),
51 }
52 }
53
54 pub fn from_vecs(coefficients: Vec<u8>, payload: Vec<u8>) -> Self {
56 Self {
57 coefficients: AlignedBuffer::from_slice(&coefficients),
58 payload: AlignedBuffer::from_slice(&payload),
59 }
60 }
61}
62
63pub struct SimpleRng(u64);
69
70impl SimpleRng {
71 pub fn new(seed: u64) -> Self {
73 SimpleRng(seed.wrapping_add(1))
74 }
75 pub fn next_u8(&mut self) -> u8 {
77 self.0 ^= self.0 << 13;
78 self.0 ^= self.0 >> 7;
79 self.0 ^= self.0 << 17;
80 self.0 as u8
81 }
82 pub fn fill(&mut self, buf: &mut [u8]) {
84 for b in buf.iter_mut() {
85 *b = self.next_u8();
86 }
87 }
88}
89
90#[cfg(feature = "alloc")]
92pub struct Encoder {
93 generation_size: usize,
94 symbol_size: usize,
95}
96
97#[cfg(feature = "alloc")]
98impl Encoder {
99 pub fn new(generation_size: usize, symbol_size: usize) -> Result<Self, RlncError> {
104 if generation_size == 0 || symbol_size == 0 {
105 return Err(RlncError::InvalidParameters);
106 }
107 Ok(Encoder {
108 generation_size,
109 symbol_size,
110 })
111 }
112
113 pub fn generation_size(&self) -> usize {
115 self.generation_size
116 }
117 pub fn symbol_size(&self) -> usize {
119 self.symbol_size
120 }
121
122 pub fn encode_random(
130 &self,
131 source: &[&[u8]],
132 rng: &mut SimpleRng,
133 ) -> Result<CodedPacket, RlncError> {
134 self.validate_source(source)?;
135
136 let k = self.generation_size;
137 let n = self.symbol_size;
138
139 let mut coeffs = AlignedBuffer::zeroed(k);
140 let mut payload = AlignedBuffer::zeroed(n);
141
142 rng.fill(coeffs.as_mut_slice());
143
144 let mut retries = 0u32;
146 while coeffs.as_slice().iter().all(|&c| c == 0) {
147 retries += 1;
148 if retries >= 100 {
149 coeffs.as_mut_slice()[0] = 1;
150 break;
151 }
152 rng.fill(coeffs.as_mut_slice());
153 }
154
155 kernel::axpy_multi(coeffs.as_slice(), source, payload.as_mut_slice());
156
157 Ok(CodedPacket {
158 coefficients: coeffs,
159 payload,
160 })
161 }
162
163 pub fn encode_systematic(
165 &self,
166 source: &[&[u8]],
167 index: usize,
168 ) -> Result<CodedPacket, RlncError> {
169 self.validate_source(source)?;
170 if index >= self.generation_size {
171 return Err(RlncError::IndexOutOfRange {
172 index,
173 max: self.generation_size,
174 });
175 }
176
177 let mut coeffs = AlignedBuffer::zeroed(self.generation_size);
178 coeffs.as_mut_slice()[index] = 1;
179 let payload = AlignedBuffer::from_slice(source[index]);
180
181 Ok(CodedPacket {
182 coefficients: coeffs,
183 payload,
184 })
185 }
186
187 fn validate_source(&self, source: &[&[u8]]) -> Result<(), RlncError> {
188 if source.len() != self.generation_size {
189 return Err(RlncError::SourceCountMismatch {
190 expected: self.generation_size,
191 got: source.len(),
192 });
193 }
194 for s in source {
195 if s.len() != self.symbol_size {
196 return Err(RlncError::SourceSizeMismatch {
197 expected: self.symbol_size,
198 got: s.len(),
199 });
200 }
201 }
202 Ok(())
203 }
204}
205
206#[cfg(test)]
207#[cfg(feature = "alloc")]
208mod tests {
209 use super::*;
210 use crate::aligned::ALIGN;
211
212 #[test]
213 fn systematic_packet_is_copy() {
214 let k = 4usize;
215 let n = 8usize;
216 let symbols: Vec<Vec<u8>> = (0..k as u8).map(|i| vec![i * 10; n]).collect();
217 let refs: Vec<&[u8]> = symbols.iter().map(Vec::as_slice).collect();
218
219 let enc = Encoder::new(k, n).unwrap();
220 for (i, symbol) in symbols.iter().enumerate() {
221 let pkt = enc.encode_systematic(&refs, i).unwrap();
222 assert_eq!(pkt.payload.as_slice(), symbol.as_slice());
223 assert_eq!(pkt.coefficients.as_slice()[i], 1);
224 assert!(pkt
225 .coefficients
226 .as_slice()
227 .iter()
228 .enumerate()
229 .all(|(j, &c)| if j == i { c == 1 } else { c == 0 }));
230 }
231 }
232
233 #[test]
234 fn random_encode_non_zero_payload() {
235 let k = 4usize;
236 let n = 16usize;
237 let symbols: Vec<Vec<u8>> = (1u8..=k as u8).map(|i| vec![i; n]).collect();
238 let refs: Vec<&[u8]> = symbols.iter().map(Vec::as_slice).collect();
239
240 let enc = Encoder::new(k, n).unwrap();
241 let mut rng = SimpleRng::new(42);
242 let pkt = enc.encode_random(&refs, &mut rng).unwrap();
243 assert_eq!(pkt.payload.len(), n);
244 assert_eq!(pkt.coefficients.len(), k);
245 }
246
247 #[test]
248 fn coded_packet_payload_is_aligned() {
249 let k = 4usize;
250 let n = 128usize;
251 let symbols: Vec<Vec<u8>> = (0..k as u8).map(|i| vec![i; n]).collect();
252 let refs: Vec<&[u8]> = symbols.iter().map(Vec::as_slice).collect();
253
254 let enc = Encoder::new(k, n).unwrap();
255 let mut rng = SimpleRng::new(0xBEEF);
256 let pkt = enc.encode_random(&refs, &mut rng).unwrap();
257 assert_eq!(
258 pkt.payload.as_ptr() as usize % ALIGN,
259 0,
260 "CodedPacket.payload must be {ALIGN}-byte aligned"
261 );
262 assert_eq!(
263 pkt.coefficients.as_ptr() as usize % ALIGN,
264 0,
265 "CodedPacket.coefficients must be {ALIGN}-byte aligned"
266 );
267 }
268
269 #[test]
270 fn coded_packet_into_from_vecs_roundtrip() {
271 let pkt = CodedPacket::from_slices(&[1, 2, 3, 4], &[9, 8, 7, 6]);
272 let (c, p) = pkt.into_vecs();
273 assert_eq!(c, vec![1, 2, 3, 4]);
274 assert_eq!(p, vec![9, 8, 7, 6]);
275 let pkt2 = CodedPacket::from_vecs(c, p);
276 assert_eq!(pkt2.coefficients_slice(), &[1, 2, 3, 4]);
277 assert_eq!(pkt2.payload_slice(), &[9, 8, 7, 6]);
278 }
279
280 #[test]
281 fn new_rejects_zero_params() {
282 assert!(Encoder::new(0, 16).is_err());
283 assert!(Encoder::new(4, 0).is_err());
284 assert!(matches!(
285 Encoder::new(0, 0),
286 Err(crate::error::RlncError::InvalidParameters)
287 ));
288 }
289
290 #[test]
291 fn encode_rejects_source_count_mismatch() {
292 let enc = Encoder::new(2, 4).unwrap();
293 let s0 = [1u8; 4];
294 let mut rng = SimpleRng::new(1);
295 let err = enc.encode_random(&[&s0], &mut rng).unwrap_err();
296 match err {
297 crate::error::RlncError::SourceCountMismatch {
298 expected: 2,
299 got: 1,
300 } => {}
301 other => panic!("unexpected {other:?}"),
302 }
303 }
304
305 #[test]
306 fn encode_rejects_source_size_mismatch() {
307 let enc = Encoder::new(2, 4).unwrap();
308 let s0 = [1u8; 4];
309 let s1 = [2u8; 3];
310 let mut rng = SimpleRng::new(1);
311 let err = enc.encode_random(&[&s0, &s1], &mut rng).unwrap_err();
312 match err {
313 crate::error::RlncError::SourceSizeMismatch {
314 expected: 4,
315 got: 3,
316 } => {}
317 other => panic!("unexpected {other:?}"),
318 }
319 }
320
321 #[test]
322 fn systematic_index_out_of_range() {
323 let enc = Encoder::new(2, 4).unwrap();
324 let s0 = [1u8; 4];
325 let s1 = [2u8; 4];
326 let err = enc.encode_systematic(&[&s0, &s1], 2).unwrap_err();
327 match err {
328 crate::error::RlncError::IndexOutOfRange { index: 2, max: 2 } => {}
329 other => panic!("unexpected {other:?}"),
330 }
331 }
332}