reed_solomon_simd/rate/
rate_high.rs

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
9// ======================================================================
10// HighRate - PUBLIC
11
12/// Reed-Solomon encoder/decoder generator using only high rate.
13pub 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
28// ======================================================================
29// HighRateEncoder - PUBLIC
30
31/// Reed-Solomon encoder using only high rate.
32pub 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        // FIRST CHUNK
50
51        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            // FULL CHUNKS
58
59            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            // FINAL PARTIAL CHUNK
67
68            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        // FFT
77
78        engine.fft(&mut work, 0, chunk_size, recovery_count, 0);
79
80        // UNDO LAST CHUNK ENCODING
81
82        self.work.undo_last_chunk_encoding();
83
84        // DONE
85
86        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
115// ======================================================================
116// HighRateEncoder - PRIVATE
117
118impl<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
144// ======================================================================
145// HighRateDecoder - PUBLIC
146
147/// Reed-Solomon decoder using only high rate.
148pub 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            // Nothing to do, original data is complete.
177            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        // ERASURE LOCATIONS
185
186        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        // EVALUATE POLYNOMIAL
203
204        E::eval_poly(&mut erasures, original_end);
205
206        // MULTIPLY SHARDS
207
208        // work[               .. recovery_count] = recovery * erasures
209        // work[recovery_count .. chunk_size    ] = 0
210        // work[chunk_size     .. original_end  ] = original * erasures
211        // work[original_end   ..               ] = 0
212
213        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        // IFFT / FORMAL DERIVATIVE / FFT
234
235        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        // REVEAL ERASURES
240
241        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        // UNDO LAST CHUNK ENCODING
248
249        self.work.undo_last_chunk_encoding();
250
251        // DONE
252
253        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
282// ======================================================================
283// HighRateDecoder - PRIVATE
284
285impl<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[..recovery_count     ]  =  recovery
295        // work[recovery_count_pow2..]  =  original
296        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// ======================================================================
316// TESTS
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321    use crate::test_util;
322
323    // ============================================================
324    // ROUNDTRIPS - SINGLE ROUND
325
326    #[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    // ============================================================
421    // ROUNDTRIPS - TWO ROUNDS
422
423    #[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    // ============================================================
444    // HighRate
445
446    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    // ============================================================
513    // HighRateEncoder
514
515    mod high_rate_encoder {
516        use crate::{
517            engine::NoSimd,
518            rate::{HighRateEncoder, RateEncoder},
519            Error,
520        };
521
522        // ==================================================
523        // ERRORS
524
525        test_rate_encoder_errors! {HighRateEncoder}
526
527        // ==================================================
528        // supports
529
530        #[test]
531        fn supports() {
532            assert!(!HighRateEncoder::<NoSimd>::supports(4096, 61440));
533            assert!(HighRateEncoder::<NoSimd>::supports(61440, 4096));
534        }
535
536        // ==================================================
537        // validate
538
539        #[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        // ==================================================
558        // work_count
559
560        #[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    // ============================================================
571    // HighRateDecoder
572
573    mod high_rate_decoder {
574        use crate::{
575            engine::NoSimd,
576            rate::{HighRateDecoder, RateDecoder},
577            Error,
578        };
579
580        // ==================================================
581        // ERRORS
582
583        test_rate_decoder_errors! {HighRateDecoder}
584
585        // ==================================================
586        // supports
587
588        #[test]
589        fn supports() {
590            assert!(!HighRateDecoder::<NoSimd>::supports(4096, 61440));
591            assert!(HighRateDecoder::<NoSimd>::supports(61440, 4096));
592        }
593
594        // ==================================================
595        // validate
596
597        #[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        // ==================================================
616        // work_count
617
618        #[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}