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 HighRate<E: Engine>(PhantomData<E>);
14
15impl<E: Engine> Rate<E> for HighRate<E> {
16 type RateEncoder = HighRateEncoder<E>;
17 type RateDecoder = HighRateDecoder<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 && recovery_count.next_power_of_two() + original_count <= GF_ORDER
25 }
26}
27
28pub struct HighRateEncoder<E: Engine> {
33 engine: E,
34 work: EncoderWork,
35}
36
37impl<E: Engine> RateEncoder<E> for HighRateEncoder<E> {
38 type Rate = HighRate<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 = recovery_count.next_power_of_two();
47 let engine = &self.engine;
48
49 let first_count = std::cmp::min(original_count, chunk_size);
52
53 work.zero(first_count..chunk_size);
54 engine::ifft_skew_end(engine, &mut work, 0, chunk_size, first_count);
55
56 if original_count > chunk_size {
57 let mut chunk_start = chunk_size;
60 while chunk_start + chunk_size <= original_count {
61 engine::ifft_skew_end(engine, &mut work, chunk_start, chunk_size, chunk_size);
62 engine::xor_within(&mut work, 0, chunk_start, chunk_size);
63 chunk_start += chunk_size;
64 }
65
66 let last_count = original_count % chunk_size;
69 if last_count > 0 {
70 work.zero(chunk_start + last_count..);
71 engine::ifft_skew_end(engine, &mut work, chunk_start, chunk_size, last_count);
72 engine::xor_within(&mut work, 0, chunk_start, chunk_size);
73 }
74 }
75
76 engine.fft(&mut work, 0, chunk_size, recovery_count, 0);
79
80 self.work.undo_last_chunk_encoding();
83
84 Ok(EncoderResult::new(&mut self.work))
87 }
88
89 fn into_parts(self) -> (E, EncoderWork) {
90 (self.engine, self.work)
91 }
92
93 fn new(
94 original_count: usize,
95 recovery_count: usize,
96 shard_bytes: usize,
97 engine: E,
98 work: Option<EncoderWork>,
99 ) -> Result<Self, Error> {
100 let mut work = work.unwrap_or_default();
101 Self::reset_work(original_count, recovery_count, shard_bytes, &mut work)?;
102 Ok(Self { engine, work })
103 }
104
105 fn reset(
106 &mut self,
107 original_count: usize,
108 recovery_count: usize,
109 shard_bytes: usize,
110 ) -> Result<(), Error> {
111 Self::reset_work(original_count, recovery_count, shard_bytes, &mut self.work)
112 }
113}
114
115impl<E: Engine> HighRateEncoder<E> {
119 fn reset_work(
120 original_count: usize,
121 recovery_count: usize,
122 shard_bytes: usize,
123 work: &mut EncoderWork,
124 ) -> Result<(), Error> {
125 Self::validate(original_count, recovery_count, shard_bytes)?;
126 work.reset(
127 original_count,
128 recovery_count,
129 shard_bytes,
130 Self::work_count(original_count, recovery_count),
131 );
132 Ok(())
133 }
134
135 fn work_count(original_count: usize, recovery_count: usize) -> usize {
136 debug_assert!(Self::supports(original_count, recovery_count));
137
138 let chunk_size = recovery_count.next_power_of_two();
139
140 original_count.next_multiple_of(chunk_size)
141 }
142}
143
144pub struct HighRateDecoder<E: Engine> {
149 engine: E,
150 work: DecoderWork,
151}
152
153impl<E: Engine> RateDecoder<E> for HighRateDecoder<E> {
154 type Rate = HighRate<E>;
155
156 fn add_original_shard<T: AsRef<[u8]>>(
157 &mut self,
158 index: usize,
159 original_shard: T,
160 ) -> Result<(), Error> {
161 self.work.add_original_shard(index, original_shard)
162 }
163
164 fn add_recovery_shard<T: AsRef<[u8]>>(
165 &mut self,
166 index: usize,
167 recovery_shard: T,
168 ) -> Result<(), Error> {
169 self.work.add_recovery_shard(index, recovery_shard)
170 }
171
172 fn decode(&mut self) -> Result<DecoderResult, Error> {
173 let Some((mut work, original_count, recovery_count, received)) =
174 self.work.decode_begin()?
175 else {
176 return Ok(DecoderResult::new(&mut self.work));
178 };
179
180 let chunk_size = recovery_count.next_power_of_two();
181 let original_end = chunk_size + original_count;
182 let work_count = work.len();
183
184 let mut erasures = [0; GF_ORDER];
187
188 for i in 0..recovery_count {
189 if !received[i] {
190 erasures[i] = 1;
191 }
192 }
193
194 erasures[recovery_count..chunk_size].fill(1);
195
196 for i in chunk_size..original_end {
197 if !received[i] {
198 erasures[i] = 1;
199 }
200 }
201
202 E::eval_poly(&mut erasures, original_end);
205
206 for i in 0..recovery_count {
214 if received[i] {
215 self.engine.mul(&mut work[i], erasures[i]);
216 } else {
217 work[i].fill([0; 64]);
218 }
219 }
220
221 work.zero(recovery_count..chunk_size);
222
223 for i in chunk_size..original_end {
224 if received[i] {
225 self.engine.mul(&mut work[i], erasures[i]);
226 } else {
227 work[i].fill([0; 64]);
228 }
229 }
230
231 work.zero(original_end..);
232
233 self.engine.ifft(&mut work, 0, work_count, original_end, 0);
236 engine::formal_derivative(&mut work);
237 self.engine.fft(&mut work, 0, work_count, original_end, 0);
238
239 for i in chunk_size..original_end {
242 if !received[i] {
243 self.engine.mul(&mut work[i], GF_MODULUS - erasures[i]);
244 }
245 }
246
247 self.work.undo_last_chunk_encoding();
250
251 Ok(DecoderResult::new(&mut self.work))
254 }
255
256 fn into_parts(self) -> (E, DecoderWork) {
257 (self.engine, self.work)
258 }
259
260 fn new(
261 original_count: usize,
262 recovery_count: usize,
263 shard_bytes: usize,
264 engine: E,
265 work: Option<DecoderWork>,
266 ) -> Result<Self, Error> {
267 let mut work = work.unwrap_or_default();
268 Self::reset_work(original_count, recovery_count, shard_bytes, &mut work)?;
269 Ok(Self { engine, work })
270 }
271
272 fn reset(
273 &mut self,
274 original_count: usize,
275 recovery_count: usize,
276 shard_bytes: usize,
277 ) -> Result<(), Error> {
278 Self::reset_work(original_count, recovery_count, shard_bytes, &mut self.work)
279 }
280}
281
282impl<E: Engine> HighRateDecoder<E> {
286 fn reset_work(
287 original_count: usize,
288 recovery_count: usize,
289 shard_bytes: usize,
290 work: &mut DecoderWork,
291 ) -> Result<(), Error> {
292 Self::validate(original_count, recovery_count, shard_bytes)?;
293
294 work.reset(
297 original_count,
298 recovery_count,
299 shard_bytes,
300 recovery_count.next_power_of_two(),
301 0,
302 Self::work_count(original_count, recovery_count),
303 );
304
305 Ok(())
306 }
307
308 fn work_count(original_count: usize, recovery_count: usize) -> usize {
309 debug_assert!(Self::supports(original_count, recovery_count));
310
311 (recovery_count.next_power_of_two() + original_count).next_power_of_two()
312 }
313}
314
315#[cfg(test)]
319mod tests {
320 use super::*;
321 use crate::test_util;
322
323 #[test]
327 fn roundtrip_all_originals_missing() {
328 roundtrip_single!(
329 HighRate,
330 3,
331 3,
332 1024,
333 test_util::EITHER_3_3,
334 &[],
335 &[0..3],
336 133,
337 );
338 }
339
340 #[test]
341 fn roundtrip_no_originals_missing() {
342 roundtrip_single!(HighRate, 3, 2, 1024, test_util::HIGH_3_2, &[0..3], &[], 132);
343 }
344
345 #[test]
346 fn roundtrips_tiny() {
347 for (original_count, recovery_count, seed, recovery_hash) in test_util::HIGH_TINY {
348 roundtrip_single!(
349 HighRate,
350 *original_count,
351 *recovery_count,
352 1024,
353 recovery_hash,
354 &[*recovery_count..*original_count],
355 &[0..std::cmp::min(*original_count, *recovery_count)],
356 *seed,
357 );
358 }
359 }
360
361 #[test]
362 #[ignore]
363 fn roundtrip_3000_30000() {
364 roundtrip_single!(
365 HighRate,
366 3000,
367 30000,
368 64,
369 test_util::HIGH_3000_30000_14,
370 &[],
371 &[0..3000],
372 14,
373 );
374 }
375
376 #[test]
377 #[ignore]
378 fn roundtrip_32768_32768() {
379 roundtrip_single!(
380 HighRate,
381 32768,
382 32768,
383 64,
384 test_util::EITHER_32768_32768_11,
385 &[],
386 &[0..32768],
387 11,
388 );
389 }
390
391 #[test]
392 #[ignore]
393 fn roundtrip_60000_3000() {
394 roundtrip_single!(
395 HighRate,
396 60000,
397 3000,
398 64,
399 test_util::HIGH_60000_3000_12,
400 &[3000..60000],
401 &[0..3000],
402 12,
403 );
404 }
405
406 #[test]
407 fn roundtrip_34000_2000_shard_size_8() {
408 roundtrip_single!(
409 HighRate,
410 34000,
411 2000,
412 8,
413 test_util::HIGH_34000_2000_123_8,
414 &[0..32000],
415 &[0..2000],
416 123
417 );
418 }
419
420 #[test]
424 fn two_rounds_implicit_reset() {
425 roundtrip_two_rounds!(
426 HighRate,
427 false,
428 (3, 2, 1024, test_util::HIGH_3_2, &[1], &[0, 1], 132),
429 (3, 2, 1024, test_util::HIGH_3_2_232, &[0], &[0, 1], 232),
430 );
431 }
432
433 #[test]
434 fn two_rounds_explicit_reset() {
435 roundtrip_two_rounds!(
436 HighRate,
437 true,
438 (3, 2, 1024, test_util::HIGH_3_2, &[1], &[0, 1], 132),
439 (5, 2, 1024, test_util::HIGH_5_2, &[0, 2, 4], &[0, 1], 152),
440 );
441 }
442
443 mod high_rate {
447 use crate::{
448 engine::NoSimd,
449 rate::{HighRate, Rate},
450 Error,
451 };
452
453 #[test]
454 fn decoder() {
455 assert_eq!(
456 HighRate::<NoSimd>::decoder(4096, 61440, 64, NoSimd::new(), None).err(),
457 Some(Error::UnsupportedShardCount {
458 original_count: 4096,
459 recovery_count: 61440,
460 })
461 );
462
463 assert!(HighRate::<NoSimd>::decoder(61440, 4096, 64, NoSimd::new(), None).is_ok());
464 }
465
466 #[test]
467 fn encoder() {
468 assert_eq!(
469 HighRate::<NoSimd>::encoder(4096, 61440, 64, NoSimd::new(), None).err(),
470 Some(Error::UnsupportedShardCount {
471 original_count: 4096,
472 recovery_count: 61440,
473 })
474 );
475
476 assert!(HighRate::<NoSimd>::encoder(61440, 4096, 64, NoSimd::new(), None).is_ok());
477 }
478
479 #[test]
480 fn supports() {
481 assert!(!HighRate::<NoSimd>::supports(0, 1));
482 assert!(!HighRate::<NoSimd>::supports(1, 0));
483
484 assert!(!HighRate::<NoSimd>::supports(4096, 61440));
485
486 assert!(HighRate::<NoSimd>::supports(61440, 4096));
487 assert!(!HighRate::<NoSimd>::supports(61440, 4097));
488 assert!(!HighRate::<NoSimd>::supports(61441, 4096));
489
490 assert!(!HighRate::<NoSimd>::supports(usize::MAX, usize::MAX));
491 }
492
493 #[test]
494 fn validate() {
495 assert_eq!(
496 HighRate::<NoSimd>::validate(1, 1, 123).err(),
497 Some(Error::InvalidShardSize { shard_bytes: 123 })
498 );
499
500 assert_eq!(
501 HighRate::<NoSimd>::validate(4096, 61440, 64).err(),
502 Some(Error::UnsupportedShardCount {
503 original_count: 4096,
504 recovery_count: 61440,
505 })
506 );
507
508 assert!(HighRate::<NoSimd>::validate(61440, 4096, 64).is_ok());
509 }
510 }
511
512 mod high_rate_encoder {
516 use crate::{
517 engine::NoSimd,
518 rate::{HighRateEncoder, RateEncoder},
519 Error,
520 };
521
522 test_rate_encoder_errors! {HighRateEncoder}
526
527 #[test]
531 fn supports() {
532 assert!(!HighRateEncoder::<NoSimd>::supports(4096, 61440));
533 assert!(HighRateEncoder::<NoSimd>::supports(61440, 4096));
534 }
535
536 #[test]
540 fn validate() {
541 assert_eq!(
542 HighRateEncoder::<NoSimd>::validate(1, 1, 123).err(),
543 Some(Error::InvalidShardSize { shard_bytes: 123 })
544 );
545
546 assert_eq!(
547 HighRateEncoder::<NoSimd>::validate(4096, 61440, 64).err(),
548 Some(Error::UnsupportedShardCount {
549 original_count: 4096,
550 recovery_count: 61440,
551 })
552 );
553
554 assert!(HighRateEncoder::<NoSimd>::validate(61440, 4096, 64).is_ok());
555 }
556
557 #[test]
561 fn work_count() {
562 assert_eq!(HighRateEncoder::<NoSimd>::work_count(1, 1), 1);
563 assert_eq!(HighRateEncoder::<NoSimd>::work_count(4096, 1024), 4096);
564 assert_eq!(HighRateEncoder::<NoSimd>::work_count(4097, 1024), 5120);
565 assert_eq!(HighRateEncoder::<NoSimd>::work_count(4097, 1025), 6144);
566 assert_eq!(HighRateEncoder::<NoSimd>::work_count(32768, 32768), 32768);
567 }
568 }
569
570 mod high_rate_decoder {
574 use crate::{
575 engine::NoSimd,
576 rate::{HighRateDecoder, RateDecoder},
577 Error,
578 };
579
580 test_rate_decoder_errors! {HighRateDecoder}
584
585 #[test]
589 fn supports() {
590 assert!(!HighRateDecoder::<NoSimd>::supports(4096, 61440));
591 assert!(HighRateDecoder::<NoSimd>::supports(61440, 4096));
592 }
593
594 #[test]
598 fn validate() {
599 assert_eq!(
600 HighRateDecoder::<NoSimd>::validate(1, 1, 123).err(),
601 Some(Error::InvalidShardSize { shard_bytes: 123 })
602 );
603
604 assert_eq!(
605 HighRateDecoder::<NoSimd>::validate(4096, 61440, 64).err(),
606 Some(Error::UnsupportedShardCount {
607 original_count: 4096,
608 recovery_count: 61440,
609 })
610 );
611
612 assert!(HighRateDecoder::<NoSimd>::validate(61440, 4096, 64).is_ok());
613 }
614
615 #[test]
619 fn work_count() {
620 assert_eq!(HighRateDecoder::<NoSimd>::work_count(1, 1), 2);
621 assert_eq!(HighRateDecoder::<NoSimd>::work_count(2048, 1025), 4096);
622 assert_eq!(HighRateDecoder::<NoSimd>::work_count(2049, 1025), 8192);
623 assert_eq!(HighRateDecoder::<NoSimd>::work_count(3072, 1024), 4096);
624 assert_eq!(HighRateDecoder::<NoSimd>::work_count(3073, 1024), 8192);
625 assert_eq!(HighRateDecoder::<NoSimd>::work_count(32768, 32768), 65536);
626 }
627 }
628}