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 let state = seed.wrapping_add(1);
74 SimpleRng(if state == 0 {
75 0x9E37_79B9_7F4A_7C15
76 } else {
77 state
78 })
79 }
80 pub fn next_u8(&mut self) -> u8 {
82 self.0 ^= self.0 << 13;
83 self.0 ^= self.0 >> 7;
84 self.0 ^= self.0 << 17;
85 self.0 as u8
86 }
87 pub fn fill(&mut self, buf: &mut [u8]) {
89 for b in buf.iter_mut() {
90 *b = self.next_u8();
91 }
92 }
93}
94
95#[cfg(feature = "alloc")]
97pub struct Encoder {
98 generation_size: usize,
99 symbol_size: usize,
100}
101
102#[cfg(feature = "alloc")]
103impl Encoder {
104 pub fn new(generation_size: usize, symbol_size: usize) -> Result<Self, RlncError> {
109 if generation_size == 0 || symbol_size == 0 {
110 return Err(RlncError::InvalidParameters);
111 }
112 Ok(Encoder {
113 generation_size,
114 symbol_size,
115 })
116 }
117
118 pub fn generation_size(&self) -> usize {
120 self.generation_size
121 }
122 pub fn symbol_size(&self) -> usize {
124 self.symbol_size
125 }
126
127 pub fn encode_random(
135 &self,
136 source: &[&[u8]],
137 rng: &mut SimpleRng,
138 ) -> Result<CodedPacket, RlncError> {
139 self.validate_source(source)?;
140
141 let k = self.generation_size;
142 let n = self.symbol_size;
143
144 let mut coeffs = AlignedBuffer::zeroed(k);
145 let mut payload = AlignedBuffer::zeroed(n);
146
147 rng.fill(coeffs.as_mut_slice());
148
149 let mut retries = 0u32;
151 while coeffs.as_slice().iter().all(|&c| c == 0) {
152 retries += 1;
153 if retries >= 100 {
154 coeffs.as_mut_slice()[0] = 1;
155 break;
156 }
157 rng.fill(coeffs.as_mut_slice());
158 }
159
160 kernel::axpy_multi(coeffs.as_slice(), source, payload.as_mut_slice());
161
162 Ok(CodedPacket {
163 coefficients: coeffs,
164 payload,
165 })
166 }
167
168 pub fn encode_systematic(
170 &self,
171 source: &[&[u8]],
172 index: usize,
173 ) -> Result<CodedPacket, RlncError> {
174 self.validate_source(source)?;
175 if index >= self.generation_size {
176 return Err(RlncError::IndexOutOfRange {
177 index,
178 max: self.generation_size,
179 });
180 }
181
182 let mut coeffs = AlignedBuffer::zeroed(self.generation_size);
183 coeffs.as_mut_slice()[index] = 1;
184 let payload = AlignedBuffer::from_slice(source[index]);
185
186 Ok(CodedPacket {
187 coefficients: coeffs,
188 payload,
189 })
190 }
191
192 fn validate_source(&self, source: &[&[u8]]) -> Result<(), RlncError> {
193 if source.len() != self.generation_size {
194 return Err(RlncError::SourceCountMismatch {
195 expected: self.generation_size,
196 got: source.len(),
197 });
198 }
199 for s in source {
200 if s.len() != self.symbol_size {
201 return Err(RlncError::SourceSizeMismatch {
202 expected: self.symbol_size,
203 got: s.len(),
204 });
205 }
206 }
207 Ok(())
208 }
209}
210
211#[cfg(test)]
212#[cfg(feature = "alloc")]
213mod tests {
214 use super::*;
215 use crate::aligned::ALIGN;
216
217 #[test]
218 fn systematic_packet_is_copy() {
219 let k = 4usize;
220 let n = 8usize;
221 let symbols: Vec<Vec<u8>> = (0..k as u8).map(|i| vec![i * 10; n]).collect();
222 let refs: Vec<&[u8]> = symbols.iter().map(Vec::as_slice).collect();
223
224 let enc = Encoder::new(k, n).unwrap();
225 for (i, symbol) in symbols.iter().enumerate() {
226 let pkt = enc.encode_systematic(&refs, i).unwrap();
227 assert_eq!(pkt.payload.as_slice(), symbol.as_slice());
228 assert_eq!(pkt.coefficients.as_slice()[i], 1);
229 assert!(pkt
230 .coefficients
231 .as_slice()
232 .iter()
233 .enumerate()
234 .all(|(j, &c)| if j == i { c == 1 } else { c == 0 }));
235 }
236 }
237
238 #[test]
239 fn max_seed_does_not_enter_zero_state() {
240 let mut rng = SimpleRng::new(u64::MAX);
241 assert_ne!(rng.next_u8(), 0);
242 let mut bytes = [0u8; 16];
243 rng.fill(&mut bytes);
244 assert!(bytes.iter().any(|&byte| byte != 0));
245 }
246
247 #[test]
248 fn random_encode_non_zero_payload() {
249 let k = 4usize;
250 let n = 16usize;
251 let symbols: Vec<Vec<u8>> = (1u8..=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(42);
256 let pkt = enc.encode_random(&refs, &mut rng).unwrap();
257 assert_eq!(pkt.payload.len(), n);
258 assert_eq!(pkt.coefficients.len(), k);
259 }
260
261 #[test]
262 fn coded_packet_payload_is_aligned() {
263 let k = 4usize;
264 let n = 128usize;
265 let symbols: Vec<Vec<u8>> = (0..k as u8).map(|i| vec![i; n]).collect();
266 let refs: Vec<&[u8]> = symbols.iter().map(Vec::as_slice).collect();
267
268 let enc = Encoder::new(k, n).unwrap();
269 let mut rng = SimpleRng::new(0xBEEF);
270 let pkt = enc.encode_random(&refs, &mut rng).unwrap();
271 assert_eq!(
272 pkt.payload.as_ptr() as usize % ALIGN,
273 0,
274 "CodedPacket.payload must be {ALIGN}-byte aligned"
275 );
276 assert_eq!(
277 pkt.coefficients.as_ptr() as usize % ALIGN,
278 0,
279 "CodedPacket.coefficients must be {ALIGN}-byte aligned"
280 );
281 }
282
283 #[test]
284 fn coded_packet_into_from_vecs_roundtrip() {
285 let pkt = CodedPacket::from_slices(&[1, 2, 3, 4], &[9, 8, 7, 6]);
286 let (c, p) = pkt.into_vecs();
287 assert_eq!(c, vec![1, 2, 3, 4]);
288 assert_eq!(p, vec![9, 8, 7, 6]);
289 let pkt2 = CodedPacket::from_vecs(c, p);
290 assert_eq!(pkt2.coefficients_slice(), &[1, 2, 3, 4]);
291 assert_eq!(pkt2.payload_slice(), &[9, 8, 7, 6]);
292 }
293
294 #[test]
295 fn new_rejects_zero_params() {
296 assert!(Encoder::new(0, 16).is_err());
297 assert!(Encoder::new(4, 0).is_err());
298 assert!(matches!(
299 Encoder::new(0, 0),
300 Err(crate::error::RlncError::InvalidParameters)
301 ));
302 }
303
304 #[test]
305 fn encode_rejects_source_count_mismatch() {
306 let enc = Encoder::new(2, 4).unwrap();
307 let s0 = [1u8; 4];
308 let mut rng = SimpleRng::new(1);
309 let err = enc.encode_random(&[&s0], &mut rng).unwrap_err();
310 match err {
311 crate::error::RlncError::SourceCountMismatch {
312 expected: 2,
313 got: 1,
314 } => {}
315 other => panic!("unexpected {other:?}"),
316 }
317 }
318
319 #[test]
320 fn encode_rejects_source_size_mismatch() {
321 let enc = Encoder::new(2, 4).unwrap();
322 let s0 = [1u8; 4];
323 let s1 = [2u8; 3];
324 let mut rng = SimpleRng::new(1);
325 let err = enc.encode_random(&[&s0, &s1], &mut rng).unwrap_err();
326 match err {
327 crate::error::RlncError::SourceSizeMismatch {
328 expected: 4,
329 got: 3,
330 } => {}
331 other => panic!("unexpected {other:?}"),
332 }
333 }
334
335 #[test]
336 fn systematic_index_out_of_range() {
337 let enc = Encoder::new(2, 4).unwrap();
338 let s0 = [1u8; 4];
339 let s1 = [2u8; 4];
340 let err = enc.encode_systematic(&[&s0, &s1], 2).unwrap_err();
341 match err {
342 crate::error::RlncError::IndexOutOfRange { index: 2, max: 2 } => {}
343 other => panic!("unexpected {other:?}"),
344 }
345 }
346}