1use std::marker::PhantomData;
2
3use crate::{
4 engine::{self, Engine, GF_MODULUS, GF_ORDER},
5 rate::{DecoderWork, EncoderWork, Rate, RateDecoder, RateEncoder},
6 DecoderResult, EncoderResult, Error,
7};
8
9pub struct LowRate<E: Engine>(PhantomData<E>);
14
15impl<E: Engine> Rate<E> for LowRate<E> {
16 type RateEncoder = LowRateEncoder<E>;
17 type RateDecoder = LowRateDecoder<E>;
18
19 fn supports(original_count: usize, recovery_count: usize) -> bool {
20 original_count > 0
21 && recovery_count > 0
22 && original_count < GF_ORDER
23 && recovery_count < GF_ORDER
24 && original_count.next_power_of_two() + recovery_count <= GF_ORDER
25 }
26}
27
28pub struct LowRateEncoder<E: Engine> {
33 engine: E,
34 work: EncoderWork,
35}
36
37impl<E: Engine> RateEncoder<E> for LowRateEncoder<E> {
38 type Rate = LowRate<E>;
39
40 fn add_original_shard<T: AsRef<[u8]>>(&mut self, original_shard: T) -> Result<(), Error> {
41 self.work.add_original_shard(original_shard)
42 }
43
44 fn encode(&mut self) -> Result<EncoderResult, Error> {
45 let (mut work, original_count, recovery_count) = self.work.encode_begin()?;
46 let chunk_size = original_count.next_power_of_two();
47 let engine = &self.engine;
48
49 work.zero(original_count..chunk_size);
52
53 engine.ifft(&mut work, 0, chunk_size, original_count, 0);
56
57 let mut chunk_start = chunk_size;
60 while chunk_start < recovery_count {
61 work.copy_within(0, chunk_start, chunk_size);
62 chunk_start += chunk_size;
63 }
64
65 let mut chunk_start = 0;
68 while chunk_start + chunk_size <= recovery_count {
69 engine.fft_skew_end(&mut work, chunk_start, chunk_size, chunk_size);
70 chunk_start += chunk_size;
71 }
72
73 let last_count = recovery_count % chunk_size;
76 if last_count > 0 {
77 engine.fft_skew_end(&mut work, chunk_start, chunk_size, last_count);
78 }
79
80 Ok(EncoderResult::new(&mut self.work))
83 }
84
85 fn into_parts(self) -> (E, EncoderWork) {
86 (self.engine, self.work)
87 }
88
89 fn new(
90 original_count: usize,
91 recovery_count: usize,
92 shard_bytes: usize,
93 engine: E,
94 work: Option<EncoderWork>,
95 ) -> Result<Self, Error> {
96 let mut work = work.unwrap_or_default();
97 Self::reset_work(original_count, recovery_count, shard_bytes, &mut work)?;
98 Ok(Self { work, engine })
99 }
100
101 fn reset(
102 &mut self,
103 original_count: usize,
104 recovery_count: usize,
105 shard_bytes: usize,
106 ) -> Result<(), Error> {
107 Self::reset_work(original_count, recovery_count, shard_bytes, &mut self.work)
108 }
109}
110
111impl<E: Engine> LowRateEncoder<E> {
115 fn reset_work(
116 original_count: usize,
117 recovery_count: usize,
118 shard_bytes: usize,
119 work: &mut EncoderWork,
120 ) -> Result<(), Error> {
121 Self::validate(original_count, recovery_count, shard_bytes)?;
122 work.reset(
123 original_count,
124 recovery_count,
125 shard_bytes,
126 Self::work_count(original_count, recovery_count),
127 );
128 Ok(())
129 }
130
131 fn work_count(original_count: usize, recovery_count: usize) -> usize {
132 debug_assert!(Self::supports(original_count, recovery_count));
133
134 let chunk_size = original_count.next_power_of_two();
135
136 engine::checked_next_multiple_of(recovery_count, chunk_size).unwrap()
137 }
138}
139
140pub struct LowRateDecoder<E: Engine> {
145 engine: E,
146 work: DecoderWork,
147}
148
149impl<E: Engine> RateDecoder<E> for LowRateDecoder<E> {
150 type Rate = LowRate<E>;
151
152 fn add_original_shard<T: AsRef<[u8]>>(
153 &mut self,
154 index: usize,
155 original_shard: T,
156 ) -> Result<(), Error> {
157 self.work.add_original_shard(index, original_shard)
158 }
159
160 fn add_recovery_shard<T: AsRef<[u8]>>(
161 &mut self,
162 index: usize,
163 recovery_shard: T,
164 ) -> Result<(), Error> {
165 self.work.add_recovery_shard(index, recovery_shard)
166 }
167
168 fn decode(&mut self) -> Result<DecoderResult, Error> {
169 let (mut work, original_count, recovery_count, received) =
170 if let Some(stuff) = self.work.decode_begin()? {
171 stuff
172 } else {
173 return Ok(DecoderResult::new(&mut self.work));
175 };
176
177 let chunk_size = original_count.next_power_of_two();
178 let recovery_end = chunk_size + recovery_count;
179 let work_count = work.len();
180
181 let mut erasures = [0; GF_ORDER];
184
185 for i in 0..original_count {
186 if !received[i] {
187 erasures[i] = 1;
188 }
189 }
190
191 for i in chunk_size..recovery_end {
192 if !received[i] {
193 erasures[i] = 1;
194 }
195 }
196
197 erasures[recovery_end..].fill(1);
198
199 E::eval_poly(&mut erasures, GF_ORDER);
202
203 for i in 0..original_count {
211 if received[i] {
212 self.engine.mul(&mut work[i], erasures[i]);
213 } else {
214 work[i].fill(0);
215 }
216 }
217
218 work.zero(original_count..chunk_size);
219
220 for i in chunk_size..recovery_end {
221 if received[i] {
222 self.engine.mul(&mut work[i], erasures[i]);
223 } else {
224 work[i].fill(0);
225 }
226 }
227
228 work.zero(recovery_end..);
229
230 self.engine.ifft(&mut work, 0, work_count, recovery_end, 0);
233 E::formal_derivative(&mut work);
234 self.engine.fft(&mut work, 0, work_count, recovery_end, 0);
235
236 for i in 0..original_count {
239 if !received[i] {
240 self.engine.mul(&mut work[i], GF_MODULUS - erasures[i]);
241 }
242 }
243
244 Ok(DecoderResult::new(&mut self.work))
247 }
248
249 fn into_parts(self) -> (E, DecoderWork) {
250 (self.engine, self.work)
251 }
252
253 fn new(
254 original_count: usize,
255 recovery_count: usize,
256 shard_bytes: usize,
257 engine: E,
258 work: Option<DecoderWork>,
259 ) -> Result<Self, Error> {
260 let mut work = work.unwrap_or_default();
261 Self::reset_work(original_count, recovery_count, shard_bytes, &mut work)?;
262 Ok(Self { work, engine })
263 }
264
265 fn reset(
266 &mut self,
267 original_count: usize,
268 recovery_count: usize,
269 shard_bytes: usize,
270 ) -> Result<(), Error> {
271 Self::reset_work(original_count, recovery_count, shard_bytes, &mut self.work)
272 }
273}
274
275impl<E: Engine> LowRateDecoder<E> {
279 fn reset_work(
280 original_count: usize,
281 recovery_count: usize,
282 shard_bytes: usize,
283 work: &mut DecoderWork,
284 ) -> Result<(), Error> {
285 Self::validate(original_count, recovery_count, shard_bytes)?;
286
287 work.reset(
290 original_count,
291 recovery_count,
292 shard_bytes,
293 0,
294 original_count.next_power_of_two(),
295 Self::work_count(original_count, recovery_count),
296 );
297
298 Ok(())
299 }
300
301 fn work_count(original_count: usize, recovery_count: usize) -> usize {
302 debug_assert!(Self::supports(original_count, recovery_count));
303
304 (original_count.next_power_of_two() + recovery_count).next_power_of_two()
305 }
306}
307
308#[cfg(test)]
312mod tests {
313 use super::*;
314 use crate::test_util;
315
316 #[test]
320 fn roundtrip_all_originals_missing() {
321 roundtrip_single!(
322 LowRate,
323 3,
324 3,
325 1024,
326 test_util::EITHER_3_3,
327 &[],
328 &[0..3],
329 133
330 );
331 }
332
333 #[test]
334 fn roundtrip_no_originals_missing() {
335 roundtrip_single!(LowRate, 2, 3, 1024, test_util::LOW_2_3, &[0, 1], &[], 123);
336 }
337
338 #[test]
339 fn roundtrips_tiny() {
340 for (original_count, recovery_count, seed, recovery_hash) in test_util::LOW_TINY {
341 roundtrip_single!(
342 LowRate,
343 *original_count,
344 *recovery_count,
345 1024,
346 recovery_hash,
347 &[*recovery_count..*original_count],
348 &[0..std::cmp::min(*original_count, *recovery_count)],
349 *seed,
350 );
351 }
352 }
353
354 #[test]
355 #[ignore]
356 fn roundtrip_3000_60000() {
357 roundtrip_single!(
358 LowRate,
359 3000,
360 60000,
361 64,
362 test_util::LOW_3000_60000_13,
363 &[],
364 &[0..3000],
365 13,
366 );
367 }
368
369 #[test]
370 #[ignore]
371 fn roundtrip_30000_3000() {
372 roundtrip_single!(
373 LowRate,
374 30000,
375 3000,
376 64,
377 test_util::LOW_30000_3000_15,
378 &[3000..30000],
379 &[0..3000],
380 15,
381 );
382 }
383
384 #[test]
385 #[ignore]
386 fn roundtrip_32768_32768() {
387 roundtrip_single!(
388 LowRate,
389 32768,
390 32768,
391 64,
392 test_util::EITHER_32768_32768_11,
393 &[],
394 &[0..32768],
395 11,
396 );
397 }
398
399 #[test]
403 fn two_rounds_implicit_reset() {
404 roundtrip_two_rounds!(
405 LowRate,
406 false,
407 (2, 3, 1024, test_util::LOW_2_3, &[], &[0, 2], 123),
408 (2, 3, 1024, test_util::LOW_2_3_223, &[], &[1, 2], 223),
409 );
410 }
411
412 #[test]
413 fn two_rounds_explicit_reset() {
414 roundtrip_two_rounds!(
415 LowRate,
416 true,
417 (2, 3, 1024, test_util::LOW_2_3, &[], &[0, 2], 123),
418 (2, 5, 1024, test_util::LOW_2_5, &[], &[0, 4], 125),
419 );
420 }
421
422 mod low_rate {
426 use crate::{
427 engine::NoSimd,
428 rate::{LowRate, Rate},
429 Error,
430 };
431
432 #[test]
433 fn decoder() {
434 assert!(LowRate::<NoSimd>::decoder(4096, 61440, 64, NoSimd::new(), None).is_ok());
435
436 assert_eq!(
437 LowRate::<NoSimd>::decoder(61440, 4096, 64, NoSimd::new(), None).err(),
438 Some(Error::UnsupportedShardCount {
439 original_count: 61440,
440 recovery_count: 4096,
441 })
442 );
443 }
444
445 #[test]
446 fn encoder() {
447 assert!(LowRate::<NoSimd>::encoder(4096, 61440, 64, NoSimd::new(), None).is_ok());
448
449 assert_eq!(
450 LowRate::<NoSimd>::encoder(61440, 4096, 64, NoSimd::new(), None).err(),
451 Some(Error::UnsupportedShardCount {
452 original_count: 61440,
453 recovery_count: 4096,
454 })
455 );
456 }
457
458 #[test]
459 fn supports() {
460 assert!(!LowRate::<NoSimd>::supports(0, 1));
461 assert!(!LowRate::<NoSimd>::supports(1, 0));
462
463 assert!(LowRate::<NoSimd>::supports(4096, 61440));
464 assert!(!LowRate::<NoSimd>::supports(4096, 61441));
465 assert!(!LowRate::<NoSimd>::supports(4097, 61440));
466
467 assert!(!LowRate::<NoSimd>::supports(61440, 4096));
468
469 assert!(!LowRate::<NoSimd>::supports(usize::MAX, usize::MAX));
470 }
471
472 #[test]
473 fn validate() {
474 assert_eq!(
475 LowRate::<NoSimd>::validate(1, 1, 123).err(),
476 Some(Error::InvalidShardSize { shard_bytes: 123 })
477 );
478
479 assert!(LowRate::<NoSimd>::validate(4096, 61440, 64).is_ok());
480
481 assert_eq!(
482 LowRate::<NoSimd>::validate(61440, 4096, 64).err(),
483 Some(Error::UnsupportedShardCount {
484 original_count: 61440,
485 recovery_count: 4096,
486 })
487 );
488 }
489 }
490
491 mod low_rate_encoder {
495 use crate::{
496 engine::NoSimd,
497 rate::{LowRateEncoder, RateEncoder},
498 Error,
499 };
500
501 test_rate_encoder_errors! {LowRateEncoder}
505
506 #[test]
510 fn supports() {
511 assert!(LowRateEncoder::<NoSimd>::supports(4096, 61440));
512 assert!(!LowRateEncoder::<NoSimd>::supports(61440, 4096));
513 }
514
515 #[test]
519 fn validate() {
520 assert_eq!(
521 LowRateEncoder::<NoSimd>::validate(1, 1, 123).err(),
522 Some(Error::InvalidShardSize { shard_bytes: 123 })
523 );
524
525 assert!(LowRateEncoder::<NoSimd>::validate(4096, 61440, 64).is_ok());
526
527 assert_eq!(
528 LowRateEncoder::<NoSimd>::validate(61440, 4096, 64).err(),
529 Some(Error::UnsupportedShardCount {
530 original_count: 61440,
531 recovery_count: 4096,
532 })
533 );
534 }
535
536 #[test]
540 fn work_count() {
541 assert_eq!(LowRateEncoder::<NoSimd>::work_count(1, 1), 1);
542 assert_eq!(LowRateEncoder::<NoSimd>::work_count(1024, 4096), 4096);
543 assert_eq!(LowRateEncoder::<NoSimd>::work_count(1024, 4097), 5120);
544 assert_eq!(LowRateEncoder::<NoSimd>::work_count(1025, 4097), 6144);
545 assert_eq!(LowRateEncoder::<NoSimd>::work_count(32768, 32768), 32768);
546 }
547 }
548
549 mod low_rate_decoder {
553 use crate::{
554 engine::NoSimd,
555 rate::{LowRateDecoder, RateDecoder},
556 Error,
557 };
558
559 test_rate_decoder_errors! {LowRateDecoder}
563
564 #[test]
568 fn supports() {
569 assert!(LowRateDecoder::<NoSimd>::supports(4096, 61440));
570 assert!(!LowRateDecoder::<NoSimd>::supports(61440, 4096));
571 }
572
573 #[test]
577 fn validate() {
578 assert_eq!(
579 LowRateDecoder::<NoSimd>::validate(1, 1, 123).err(),
580 Some(Error::InvalidShardSize { shard_bytes: 123 })
581 );
582
583 assert!(LowRateDecoder::<NoSimd>::validate(4096, 61440, 64).is_ok());
584
585 assert_eq!(
586 LowRateDecoder::<NoSimd>::validate(61440, 4096, 64).err(),
587 Some(Error::UnsupportedShardCount {
588 original_count: 61440,
589 recovery_count: 4096,
590 })
591 );
592 }
593
594 #[test]
598 fn work_count() {
599 assert_eq!(LowRateDecoder::<NoSimd>::work_count(1, 1), 2);
600 assert_eq!(LowRateDecoder::<NoSimd>::work_count(1024, 3072), 4096);
601 assert_eq!(LowRateDecoder::<NoSimd>::work_count(1024, 3073), 8192);
602 assert_eq!(LowRateDecoder::<NoSimd>::work_count(1025, 2048), 4096);
603 assert_eq!(LowRateDecoder::<NoSimd>::work_count(1025, 2049), 8192);
604 assert_eq!(LowRateDecoder::<NoSimd>::work_count(32768, 32768), 65536);
605 }
606 }
607}