1use crate::reed_solomon::{
2 engine::{self, Engine, GF_MODULUS, GF_ORDER, SHARD_CHUNK_BYTES},
3 rate::{DecoderWork, EncoderWork, Rate, RateDecoder, RateEncoder},
4 DecoderResult, EncoderResult, Error,
5};
6use core::marker::PhantomData;
7
8pub struct HighRate<E: Engine>(PhantomData<E>);
13
14impl<E: Engine> Rate<E> for HighRate<E> {
15 type RateEncoder = HighRateEncoder<E>;
16 type RateDecoder = HighRateDecoder<E>;
17
18 fn supports(original_count: usize, recovery_count: usize) -> bool {
19 original_count > 0
20 && recovery_count > 0
21 && original_count < GF_ORDER
22 && recovery_count < GF_ORDER
23 && recovery_count.next_power_of_two() + original_count <= GF_ORDER
24 }
25}
26
27pub struct HighRateEncoder<E: Engine> {
32 engine: E,
33 work: EncoderWork,
34}
35
36impl<E: Engine> RateEncoder<E> for HighRateEncoder<E> {
37 type Rate = HighRate<E>;
38
39 fn add_original_shard<T: AsRef<[u8]>>(&mut self, original_shard: T) -> Result<(), Error> {
40 self.work.add_original_shard(original_shard)
41 }
42
43 fn encode(&mut self) -> Result<EncoderResult<'_>, Error> {
44 let (mut work, original_count, recovery_count) = self.work.encode_begin()?;
45 let chunk_size = recovery_count.next_power_of_two();
46 let engine = &self.engine;
47
48 let first_count = core::cmp::min(original_count, chunk_size);
51
52 work.zero(first_count..chunk_size);
53 engine::ifft_skew_end(engine, &mut work, 0, chunk_size, first_count);
54
55 if original_count > chunk_size {
56 let mut chunk_start = chunk_size;
59 while chunk_start + chunk_size <= original_count {
60 engine::ifft_skew_end(engine, &mut work, chunk_start, chunk_size, chunk_size);
61 engine::xor_within(&mut work, 0, chunk_start, chunk_size);
62 chunk_start += chunk_size;
63 }
64
65 let last_count = original_count % chunk_size;
68 if last_count > 0 {
69 work.zero(chunk_start + last_count..);
70 engine::ifft_skew_end(engine, &mut work, chunk_start, chunk_size, last_count);
71 engine::xor_within(&mut work, 0, chunk_start, chunk_size);
72 }
73 }
74
75 engine.fft(&mut work, 0, chunk_size, recovery_count, 0);
78
79 self.work.undo_last_chunk_encoding();
82
83 Ok(EncoderResult::new(&mut self.work))
86 }
87
88 fn into_parts(self) -> (E, EncoderWork) {
89 (self.engine, self.work)
90 }
91
92 fn new(
93 original_count: usize,
94 recovery_count: usize,
95 shard_bytes: usize,
96 engine: E,
97 work: Option<EncoderWork>,
98 ) -> Result<Self, Error> {
99 let mut work = work.unwrap_or_default();
100 Self::reset_work(original_count, recovery_count, shard_bytes, &mut work)?;
101 Ok(Self { engine, work })
102 }
103
104 fn reset(
105 &mut self,
106 original_count: usize,
107 recovery_count: usize,
108 shard_bytes: usize,
109 ) -> Result<(), Error> {
110 Self::reset_work(original_count, recovery_count, shard_bytes, &mut self.work)
111 }
112}
113
114impl<E: Engine> HighRateEncoder<E> {
118 fn reset_work(
119 original_count: usize,
120 recovery_count: usize,
121 shard_bytes: usize,
122 work: &mut EncoderWork,
123 ) -> Result<(), Error> {
124 Self::validate(original_count, recovery_count, shard_bytes)?;
125 work.reset(
126 original_count,
127 recovery_count,
128 shard_bytes,
129 Self::work_count(original_count, recovery_count),
130 );
131 Ok(())
132 }
133
134 fn work_count(original_count: usize, recovery_count: usize) -> usize {
135 assert!(Self::supports(original_count, recovery_count));
136
137 let chunk_size = recovery_count.next_power_of_two();
138
139 original_count.next_multiple_of(chunk_size)
140 }
141}
142
143pub struct HighRateDecoder<E: Engine> {
148 engine: E,
149 work: DecoderWork,
150}
151
152impl<E: Engine> RateDecoder<E> for HighRateDecoder<E> {
153 type Rate = HighRate<E>;
154
155 fn add_original_shard<T: AsRef<[u8]>>(
156 &mut self,
157 index: usize,
158 original_shard: T,
159 ) -> Result<(), Error> {
160 self.work.add_original_shard(index, original_shard)
161 }
162
163 fn add_recovery_shard<T: AsRef<[u8]>>(
164 &mut self,
165 index: usize,
166 recovery_shard: T,
167 ) -> Result<(), Error> {
168 self.work.add_recovery_shard(index, recovery_shard)
169 }
170
171 fn decode(&mut self, compute_recovery: bool) -> Result<Option<DecoderResult<'_>>, Error> {
172 let Some((mut work, original_count, recovery_count, received)) =
173 self.work.decode_begin()?
174 else {
175 self.work.reset_received();
178 return Ok(None);
179 };
180
181 let chunk_size = recovery_count.next_power_of_two();
182 let original_end = chunk_size + original_count;
183 let work_count = work.len();
184
185 let mut erasures = [0; GF_ORDER];
188
189 for i in 0..recovery_count {
190 if !received[i] {
191 erasures[i] = 1;
192 }
193 }
194
195 erasures[recovery_count..chunk_size].fill(1);
196
197 for i in chunk_size..original_end {
198 if !received[i] {
199 erasures[i] = 1;
200 }
201 }
202
203 E::eval_poly(&mut erasures, original_end);
206
207 for i in 0..recovery_count {
215 if received[i] {
216 self.engine.mul(&mut work[i], erasures[i]);
217 } else {
218 work[i].fill([0; SHARD_CHUNK_BYTES]);
219 }
220 }
221
222 work.zero(recovery_count..chunk_size);
223
224 for i in chunk_size..original_end {
225 if received[i] {
226 self.engine.mul(&mut work[i], erasures[i]);
227 } else {
228 work[i].fill([0; SHARD_CHUNK_BYTES]);
229 }
230 }
231
232 work.zero(original_end..);
233
234 self.engine.ifft(&mut work, 0, work_count, original_end, 0);
237 engine::formal_derivative(&mut work);
238 self.engine.fft(&mut work, 0, work_count, original_end, 0);
239
240 for i in chunk_size..original_end {
243 if !received[i] {
244 self.engine.mul(&mut work[i], GF_MODULUS - erasures[i]);
245 }
246 }
247
248 if compute_recovery {
256 for i in 0..recovery_count {
257 if !received[i] {
258 self.engine.mul(&mut work[i], GF_MODULUS - erasures[i]);
259 }
260 }
261 }
262
263 self.work.undo_last_chunk_encoding();
266 if compute_recovery {
267 self.work.undo_last_chunk_encoding_recovery();
268 }
269
270 Ok(Some(DecoderResult::new(&mut self.work)))
273 }
274
275 fn into_parts(self) -> (E, DecoderWork) {
276 (self.engine, self.work)
277 }
278
279 fn new(
280 original_count: usize,
281 recovery_count: usize,
282 shard_bytes: usize,
283 engine: E,
284 work: Option<DecoderWork>,
285 ) -> Result<Self, Error> {
286 let mut work = work.unwrap_or_default();
287 Self::reset_work(original_count, recovery_count, shard_bytes, &mut work)?;
288 Ok(Self { engine, work })
289 }
290
291 fn reset(
292 &mut self,
293 original_count: usize,
294 recovery_count: usize,
295 shard_bytes: usize,
296 ) -> Result<(), Error> {
297 Self::reset_work(original_count, recovery_count, shard_bytes, &mut self.work)
298 }
299}
300
301impl<E: Engine> HighRateDecoder<E> {
305 fn reset_work(
306 original_count: usize,
307 recovery_count: usize,
308 shard_bytes: usize,
309 work: &mut DecoderWork,
310 ) -> Result<(), Error> {
311 Self::validate(original_count, recovery_count, shard_bytes)?;
312
313 work.reset(
316 original_count,
317 recovery_count,
318 shard_bytes,
319 recovery_count.next_power_of_two(),
320 0,
321 Self::work_count(original_count, recovery_count),
322 );
323
324 Ok(())
325 }
326
327 fn work_count(original_count: usize, recovery_count: usize) -> usize {
328 assert!(Self::supports(original_count, recovery_count));
329
330 (recovery_count.next_power_of_two() + original_count).next_power_of_two()
331 }
332}
333
334#[cfg(test)]
338mod tests {
339 use super::*;
340 use crate::reed_solomon::test_util;
341
342 #[test]
346 fn roundtrip_all_originals_missing() {
347 roundtrip_single!(
348 HighRate,
349 3,
350 3,
351 1024,
352 test_util::EITHER_3_3,
353 &[],
354 &[test_util::range(0, 3)],
355 133,
356 );
357 }
358
359 #[test]
360 fn roundtrip_no_originals_missing() {
361 roundtrip_single!(
362 HighRate,
363 3,
364 2,
365 1024,
366 test_util::HIGH_3_2,
367 &[test_util::range(0, 3)],
368 &[],
369 132
370 );
371 }
372
373 #[test]
374 fn roundtrips_tiny() {
375 for (original_count, recovery_count, seed, recovery_hash) in test_util::HIGH_TINY {
376 roundtrip_single!(
377 HighRate,
378 *original_count,
379 *recovery_count,
380 1024,
381 recovery_hash,
382 &[test_util::range(*recovery_count, *original_count)],
383 &[test_util::range(
384 0,
385 core::cmp::min(*original_count, *recovery_count)
386 )],
387 *seed,
388 );
389 }
390 }
391
392 #[test]
393 #[ignore]
394 fn roundtrip_3000_30000() {
395 roundtrip_single!(
396 HighRate,
397 3000,
398 30000,
399 crate::reed_solomon::SHARD_CHUNK_BYTES,
400 test_util::HIGH_3000_30000_14,
401 &[],
402 &[test_util::range(0, 3000)],
403 14,
404 );
405 }
406
407 #[test]
408 #[ignore]
409 fn roundtrip_32768_32768() {
410 roundtrip_single!(
411 HighRate,
412 32768,
413 32768,
414 crate::reed_solomon::SHARD_CHUNK_BYTES,
415 test_util::EITHER_32768_32768_11,
416 &[],
417 &[test_util::range(0, 32768)],
418 11,
419 );
420 }
421
422 #[test]
423 #[ignore]
424 fn roundtrip_60000_3000() {
425 roundtrip_single!(
426 HighRate,
427 60000,
428 3000,
429 crate::reed_solomon::SHARD_CHUNK_BYTES,
430 test_util::HIGH_60000_3000_12,
431 &[test_util::range(3000, 60000)],
432 &[test_util::range(0, 3000)],
433 12,
434 );
435 }
436
437 #[test]
438 fn roundtrip_34000_2000_shard_size_8() {
439 roundtrip_single!(
440 HighRate,
441 34000,
442 2000,
443 8,
444 test_util::HIGH_34000_2000_123_8,
445 &[test_util::range(0, 32000)],
446 &[test_util::range(0, 2000)],
447 123
448 );
449 }
450
451 #[test]
455 fn two_rounds_implicit_reset() {
456 roundtrip_two_rounds!(
457 HighRate,
458 false,
459 (
460 3,
461 2,
462 1024,
463 test_util::HIGH_3_2,
464 &[test_util::index(1)],
465 &[test_util::index(0), test_util::index(1)],
466 132
467 ),
468 (
469 3,
470 2,
471 1024,
472 test_util::HIGH_3_2_232,
473 &[test_util::index(0)],
474 &[test_util::index(0), test_util::index(1)],
475 232
476 ),
477 );
478 }
479
480 #[test]
481 fn two_rounds_explicit_reset() {
482 roundtrip_two_rounds!(
483 HighRate,
484 true,
485 (
486 3,
487 2,
488 1024,
489 test_util::HIGH_3_2,
490 &[test_util::index(1)],
491 &[test_util::index(0), test_util::index(1)],
492 132
493 ),
494 (
495 5,
496 2,
497 1024,
498 test_util::HIGH_5_2,
499 &[
500 test_util::index(0),
501 test_util::index(2),
502 test_util::index(4)
503 ],
504 &[test_util::index(0), test_util::index(1)],
505 152
506 ),
507 );
508 }
509
510 mod high_rate {
514 use crate::reed_solomon::{
515 engine::NoSimd,
516 rate::{HighRate, Rate},
517 Error, SHARD_CHUNK_BYTES,
518 };
519
520 #[test]
521 fn decoder() {
522 assert_eq!(
523 HighRate::<NoSimd>::decoder(4096, 61440, SHARD_CHUNK_BYTES, NoSimd::new(), None)
524 .err(),
525 Some(Error::UnsupportedShardCount {
526 original_count: 4096,
527 recovery_count: 61440,
528 })
529 );
530
531 assert!(HighRate::<NoSimd>::decoder(
532 61440,
533 4096,
534 SHARD_CHUNK_BYTES,
535 NoSimd::new(),
536 None
537 )
538 .is_ok());
539 }
540
541 #[test]
542 fn encoder() {
543 assert_eq!(
544 HighRate::<NoSimd>::encoder(4096, 61440, SHARD_CHUNK_BYTES, NoSimd::new(), None)
545 .err(),
546 Some(Error::UnsupportedShardCount {
547 original_count: 4096,
548 recovery_count: 61440,
549 })
550 );
551
552 assert!(HighRate::<NoSimd>::encoder(
553 61440,
554 4096,
555 SHARD_CHUNK_BYTES,
556 NoSimd::new(),
557 None
558 )
559 .is_ok());
560 }
561
562 #[test]
563 fn supports() {
564 assert!(!HighRate::<NoSimd>::supports(0, 1));
565 assert!(!HighRate::<NoSimd>::supports(1, 0));
566
567 assert!(!HighRate::<NoSimd>::supports(4096, 61440));
568
569 assert!(HighRate::<NoSimd>::supports(61440, 4096));
570 assert!(!HighRate::<NoSimd>::supports(61440, 4097));
571 assert!(!HighRate::<NoSimd>::supports(61441, 4096));
572
573 assert!(!HighRate::<NoSimd>::supports(usize::MAX, usize::MAX));
574 }
575
576 #[test]
577 fn validate() {
578 assert_eq!(
579 HighRate::<NoSimd>::validate(1, 1, 123).err(),
580 Some(Error::InvalidShardSize { shard_bytes: 123 })
581 );
582
583 assert_eq!(
584 HighRate::<NoSimd>::validate(4096, 61440, SHARD_CHUNK_BYTES).err(),
585 Some(Error::UnsupportedShardCount {
586 original_count: 4096,
587 recovery_count: 61440,
588 })
589 );
590
591 assert!(HighRate::<NoSimd>::validate(61440, 4096, SHARD_CHUNK_BYTES).is_ok());
592 }
593 }
594
595 mod high_rate_encoder {
599 use crate::reed_solomon::{
600 engine::NoSimd,
601 rate::{HighRateEncoder, RateEncoder},
602 Error, SHARD_CHUNK_BYTES,
603 };
604
605 test_rate_encoder_errors! {HighRateEncoder}
609
610 #[test]
614 fn supports() {
615 assert!(!HighRateEncoder::<NoSimd>::supports(4096, 61440));
616 assert!(HighRateEncoder::<NoSimd>::supports(61440, 4096));
617 }
618
619 #[test]
623 fn validate() {
624 assert_eq!(
625 HighRateEncoder::<NoSimd>::validate(1, 1, 123).err(),
626 Some(Error::InvalidShardSize { shard_bytes: 123 })
627 );
628
629 assert_eq!(
630 HighRateEncoder::<NoSimd>::validate(4096, 61440, SHARD_CHUNK_BYTES).err(),
631 Some(Error::UnsupportedShardCount {
632 original_count: 4096,
633 recovery_count: 61440,
634 })
635 );
636
637 assert!(HighRateEncoder::<NoSimd>::validate(61440, 4096, SHARD_CHUNK_BYTES).is_ok());
638 }
639
640 #[test]
644 fn work_count() {
645 assert_eq!(HighRateEncoder::<NoSimd>::work_count(1, 1), 1);
646 assert_eq!(HighRateEncoder::<NoSimd>::work_count(4096, 1024), 4096);
647 assert_eq!(HighRateEncoder::<NoSimd>::work_count(4097, 1024), 5120);
648 assert_eq!(HighRateEncoder::<NoSimd>::work_count(4097, 1025), 6144);
649 assert_eq!(HighRateEncoder::<NoSimd>::work_count(32768, 32768), 32768);
650 }
651 }
652
653 mod high_rate_decoder {
657 use crate::reed_solomon::{
658 engine::NoSimd,
659 rate::{HighRateDecoder, RateDecoder},
660 Error, SHARD_CHUNK_BYTES,
661 };
662
663 test_rate_decoder_errors! {HighRateDecoder}
667
668 #[test]
672 fn supports() {
673 assert!(!HighRateDecoder::<NoSimd>::supports(4096, 61440));
674 assert!(HighRateDecoder::<NoSimd>::supports(61440, 4096));
675 }
676
677 #[test]
681 fn validate() {
682 assert_eq!(
683 HighRateDecoder::<NoSimd>::validate(1, 1, 123).err(),
684 Some(Error::InvalidShardSize { shard_bytes: 123 })
685 );
686
687 assert_eq!(
688 HighRateDecoder::<NoSimd>::validate(4096, 61440, SHARD_CHUNK_BYTES).err(),
689 Some(Error::UnsupportedShardCount {
690 original_count: 4096,
691 recovery_count: 61440,
692 })
693 );
694
695 assert!(HighRateDecoder::<NoSimd>::validate(61440, 4096, SHARD_CHUNK_BYTES).is_ok());
696 }
697
698 #[test]
702 fn work_count() {
703 assert_eq!(HighRateDecoder::<NoSimd>::work_count(1, 1), 2);
704 assert_eq!(HighRateDecoder::<NoSimd>::work_count(2048, 1025), 4096);
705 assert_eq!(HighRateDecoder::<NoSimd>::work_count(2049, 1025), 8192);
706 assert_eq!(HighRateDecoder::<NoSimd>::work_count(3072, 1024), 4096);
707 assert_eq!(HighRateDecoder::<NoSimd>::work_count(3073, 1024), 8192);
708 assert_eq!(HighRateDecoder::<NoSimd>::work_count(32768, 32768), 65536);
709 }
710 }
711}