reed_solomon_16/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(&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(&mut work, chunk_start, chunk_size, chunk_size);
62                E::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(&mut work, chunk_start, chunk_size, last_count);
72                E::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        // DONE
81
82        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
111// ======================================================================
112// HighRateEncoder - PRIVATE
113
114impl<E: Engine> HighRateEncoder<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 = recovery_count.next_power_of_two();
135
136        engine::checked_next_multiple_of(original_count, chunk_size).unwrap()
137    }
138}
139
140// ======================================================================
141// HighRateDecoder - PUBLIC
142
143/// Reed-Solomon decoder using only high rate.
144pub struct HighRateDecoder<E: Engine> {
145    engine: E,
146    work: DecoderWork,
147}
148
149impl<E: Engine> RateDecoder<E> for HighRateDecoder<E> {
150    type Rate = HighRate<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                // Nothing to do, original data is complete.
174                return Ok(DecoderResult::new(&mut self.work));
175            };
176
177        let chunk_size = recovery_count.next_power_of_two();
178        let original_end = chunk_size + original_count;
179        let work_count = work.len();
180
181        // ERASURE LOCATIONS
182
183        let mut erasures = [0; GF_ORDER];
184
185        for i in 0..recovery_count {
186            if !received[i] {
187                erasures[i] = 1;
188            }
189        }
190
191        erasures[recovery_count..chunk_size].fill(1);
192
193        for i in chunk_size..original_end {
194            if !received[i] {
195                erasures[i] = 1;
196            }
197        }
198
199        // EVALUATE POLYNOMIAL
200
201        E::eval_poly(&mut erasures, original_end);
202
203        // MULTIPLY SHARDS
204
205        // work[               .. recovery_count] = recovery * erasures
206        // work[recovery_count .. chunk_size    ] = 0
207        // work[chunk_size     .. original_end  ] = original * erasures
208        // work[original_end   ..               ] = 0
209
210        for i in 0..recovery_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(recovery_count..chunk_size);
219
220        for i in chunk_size..original_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(original_end..);
229
230        // IFFT / FORMAL DERIVATIVE / FFT
231
232        self.engine.ifft(&mut work, 0, work_count, original_end, 0);
233        E::formal_derivative(&mut work);
234        self.engine.fft(&mut work, 0, work_count, original_end, 0);
235
236        // REVEAL ERASURES
237
238        for i in chunk_size..original_end {
239            if !received[i] {
240                self.engine.mul(&mut work[i], GF_MODULUS - erasures[i]);
241            }
242        }
243
244        // DONE
245
246        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
275// ======================================================================
276// HighRateDecoder - PRIVATE
277
278impl<E: Engine> HighRateDecoder<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[..recovery_count     ]  =  recovery
288        // work[recovery_count_pow2..]  =  original
289        work.reset(
290            original_count,
291            recovery_count,
292            shard_bytes,
293            recovery_count.next_power_of_two(),
294            0,
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        (recovery_count.next_power_of_two() + original_count).next_power_of_two()
305    }
306}
307
308// ======================================================================
309// TESTS
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314    use crate::test_util;
315
316    // ============================================================
317    // ROUNDTRIPS - SINGLE ROUND
318
319    #[test]
320    fn roundtrip_all_originals_missing() {
321        roundtrip_single!(
322            HighRate,
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!(HighRate, 3, 2, 1024, test_util::HIGH_3_2, &[0..3], &[], 132);
336    }
337
338    #[test]
339    fn roundtrips_tiny() {
340        for (original_count, recovery_count, seed, recovery_hash) in test_util::HIGH_TINY {
341            roundtrip_single!(
342                HighRate,
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_30000() {
357        roundtrip_single!(
358            HighRate,
359            3000,
360            30000,
361            64,
362            test_util::HIGH_3000_30000_14,
363            &[],
364            &[0..3000],
365            14,
366        );
367    }
368
369    #[test]
370    #[ignore]
371    fn roundtrip_32768_32768() {
372        roundtrip_single!(
373            HighRate,
374            32768,
375            32768,
376            64,
377            test_util::EITHER_32768_32768_11,
378            &[],
379            &[0..32768],
380            11,
381        );
382    }
383
384    #[test]
385    #[ignore]
386    fn roundtrip_60000_3000() {
387        roundtrip_single!(
388            HighRate,
389            60000,
390            3000,
391            64,
392            test_util::HIGH_60000_3000_12,
393            &[3000..60000],
394            &[0..3000],
395            12,
396        );
397    }
398
399    // ============================================================
400    // ROUNDTRIPS - TWO ROUNDS
401
402    #[test]
403    fn two_rounds_implicit_reset() {
404        roundtrip_two_rounds!(
405            HighRate,
406            false,
407            (3, 2, 1024, test_util::HIGH_3_2, &[1], &[0, 1], 132),
408            (3, 2, 1024, test_util::HIGH_3_2_232, &[0], &[0, 1], 232),
409        );
410    }
411
412    #[test]
413    fn two_rounds_explicit_reset() {
414        roundtrip_two_rounds!(
415            HighRate,
416            true,
417            (3, 2, 1024, test_util::HIGH_3_2, &[1], &[0, 1], 132),
418            (5, 2, 1024, test_util::HIGH_5_2, &[0, 2, 4], &[0, 1], 152),
419        );
420    }
421
422    // ============================================================
423    // HighRate
424
425    mod high_rate {
426        use crate::{
427            engine::NoSimd,
428            rate::{HighRate, Rate},
429            Error,
430        };
431
432        #[test]
433        fn decoder() {
434            assert_eq!(
435                HighRate::<NoSimd>::decoder(4096, 61440, 64, NoSimd::new(), None).err(),
436                Some(Error::UnsupportedShardCount {
437                    original_count: 4096,
438                    recovery_count: 61440,
439                })
440            );
441
442            assert!(HighRate::<NoSimd>::decoder(61440, 4096, 64, NoSimd::new(), None).is_ok());
443        }
444
445        #[test]
446        fn encoder() {
447            assert_eq!(
448                HighRate::<NoSimd>::encoder(4096, 61440, 64, NoSimd::new(), None).err(),
449                Some(Error::UnsupportedShardCount {
450                    original_count: 4096,
451                    recovery_count: 61440,
452                })
453            );
454
455            assert!(HighRate::<NoSimd>::encoder(61440, 4096, 64, NoSimd::new(), None).is_ok());
456        }
457
458        #[test]
459        fn supports() {
460            assert!(!HighRate::<NoSimd>::supports(0, 1));
461            assert!(!HighRate::<NoSimd>::supports(1, 0));
462
463            assert!(!HighRate::<NoSimd>::supports(4096, 61440));
464
465            assert!(HighRate::<NoSimd>::supports(61440, 4096));
466            assert!(!HighRate::<NoSimd>::supports(61440, 4097));
467            assert!(!HighRate::<NoSimd>::supports(61441, 4096));
468
469            assert!(!HighRate::<NoSimd>::supports(usize::MAX, usize::MAX));
470        }
471
472        #[test]
473        fn validate() {
474            assert_eq!(
475                HighRate::<NoSimd>::validate(1, 1, 123).err(),
476                Some(Error::InvalidShardSize { shard_bytes: 123 })
477            );
478
479            assert_eq!(
480                HighRate::<NoSimd>::validate(4096, 61440, 64).err(),
481                Some(Error::UnsupportedShardCount {
482                    original_count: 4096,
483                    recovery_count: 61440,
484                })
485            );
486
487            assert!(HighRate::<NoSimd>::validate(61440, 4096, 64).is_ok());
488        }
489    }
490
491    // ============================================================
492    // HighRateEncoder
493
494    mod high_rate_encoder {
495        use crate::{
496            engine::NoSimd,
497            rate::{HighRateEncoder, RateEncoder},
498            Error,
499        };
500
501        // ==================================================
502        // ERRORS
503
504        test_rate_encoder_errors! {HighRateEncoder}
505
506        // ==================================================
507        // supports
508
509        #[test]
510        fn supports() {
511            assert!(!HighRateEncoder::<NoSimd>::supports(4096, 61440));
512            assert!(HighRateEncoder::<NoSimd>::supports(61440, 4096));
513        }
514
515        // ==================================================
516        // validate
517
518        #[test]
519        fn validate() {
520            assert_eq!(
521                HighRateEncoder::<NoSimd>::validate(1, 1, 123).err(),
522                Some(Error::InvalidShardSize { shard_bytes: 123 })
523            );
524
525            assert_eq!(
526                HighRateEncoder::<NoSimd>::validate(4096, 61440, 64).err(),
527                Some(Error::UnsupportedShardCount {
528                    original_count: 4096,
529                    recovery_count: 61440,
530                })
531            );
532
533            assert!(HighRateEncoder::<NoSimd>::validate(61440, 4096, 64).is_ok());
534        }
535
536        // ==================================================
537        // work_count
538
539        #[test]
540        fn work_count() {
541            assert_eq!(HighRateEncoder::<NoSimd>::work_count(1, 1), 1);
542            assert_eq!(HighRateEncoder::<NoSimd>::work_count(4096, 1024), 4096);
543            assert_eq!(HighRateEncoder::<NoSimd>::work_count(4097, 1024), 5120);
544            assert_eq!(HighRateEncoder::<NoSimd>::work_count(4097, 1025), 6144);
545            assert_eq!(HighRateEncoder::<NoSimd>::work_count(32768, 32768), 32768);
546        }
547    }
548
549    // ============================================================
550    // HighRateDecoder
551
552    mod high_rate_decoder {
553        use crate::{
554            engine::NoSimd,
555            rate::{HighRateDecoder, RateDecoder},
556            Error,
557        };
558
559        // ==================================================
560        // ERRORS
561
562        test_rate_decoder_errors! {HighRateDecoder}
563
564        // ==================================================
565        // supports
566
567        #[test]
568        fn supports() {
569            assert!(!HighRateDecoder::<NoSimd>::supports(4096, 61440));
570            assert!(HighRateDecoder::<NoSimd>::supports(61440, 4096));
571        }
572
573        // ==================================================
574        // validate
575
576        #[test]
577        fn validate() {
578            assert_eq!(
579                HighRateDecoder::<NoSimd>::validate(1, 1, 123).err(),
580                Some(Error::InvalidShardSize { shard_bytes: 123 })
581            );
582
583            assert_eq!(
584                HighRateDecoder::<NoSimd>::validate(4096, 61440, 64).err(),
585                Some(Error::UnsupportedShardCount {
586                    original_count: 4096,
587                    recovery_count: 61440,
588                })
589            );
590
591            assert!(HighRateDecoder::<NoSimd>::validate(61440, 4096, 64).is_ok());
592        }
593
594        // ==================================================
595        // work_count
596
597        #[test]
598        fn work_count() {
599            assert_eq!(HighRateDecoder::<NoSimd>::work_count(1, 1), 2);
600            assert_eq!(HighRateDecoder::<NoSimd>::work_count(2048, 1025), 4096);
601            assert_eq!(HighRateDecoder::<NoSimd>::work_count(2049, 1025), 8192);
602            assert_eq!(HighRateDecoder::<NoSimd>::work_count(3072, 1024), 4096);
603            assert_eq!(HighRateDecoder::<NoSimd>::work_count(3073, 1024), 8192);
604            assert_eq!(HighRateDecoder::<NoSimd>::work_count(32768, 32768), 65536);
605        }
606    }
607}