reed_solomon_16/rate/
rate_low.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// LowRate - PUBLIC
11
12/// Reed-Solomon encoder/decoder generator using only low rate.
13pub 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
28// ======================================================================
29// LowRateEncoder - PUBLIC
30
31/// Reed-Solomon encoder using only low rate.
32pub 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        // ZEROPAD ORIGINAL
50
51        work.zero(original_count..chunk_size);
52
53        // IFFT - ORIGINAL
54
55        engine.ifft(&mut work, 0, chunk_size, original_count, 0);
56
57        // COPY IFFT RESULT TO OTHER CHUNKS
58
59        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        // FFT - FULL CHUNKS
66
67        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        // FFT - FINAL PARTIAL CHUNK
74
75        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        // 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// LowRateEncoder - PRIVATE
113
114impl<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
140// ======================================================================
141// LowRateDecoder - PUBLIC
142
143/// Reed-Solomon decoder using only low rate.
144pub 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                // Nothing to do, original data is complete.
174                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        // ERASURE LOCATIONS
182
183        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        // EVALUATE POLYNOMIAL
200
201        E::eval_poly(&mut erasures, GF_ORDER);
202
203        // MULTIPLY SHARDS
204
205        // work[               .. original_count] = original * erasures
206        // work[original_count .. chunk_size    ] = 0
207        // work[chunk_size     .. original_end  ] = recovery * erasures
208        // work[recovery_end   ..               ] = 0
209
210        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        // IFFT / FORMAL DERIVATIVE / FFT
231
232        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        // REVEAL ERASURES
237
238        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        // 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// LowRateDecoder - PRIVATE
277
278impl<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[..original_count     ]  =  original
288        // work[original_count_pow2..]  =  recovery
289        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// ======================================================================
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            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    // ============================================================
400    // ROUNDTRIPS - TWO ROUNDS
401
402    #[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    // ============================================================
423    // LowRate
424
425    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    // ============================================================
492    // LowRateEncoder
493
494    mod low_rate_encoder {
495        use crate::{
496            engine::NoSimd,
497            rate::{LowRateEncoder, RateEncoder},
498            Error,
499        };
500
501        // ==================================================
502        // ERRORS
503
504        test_rate_encoder_errors! {LowRateEncoder}
505
506        // ==================================================
507        // supports
508
509        #[test]
510        fn supports() {
511            assert!(LowRateEncoder::<NoSimd>::supports(4096, 61440));
512            assert!(!LowRateEncoder::<NoSimd>::supports(61440, 4096));
513        }
514
515        // ==================================================
516        // validate
517
518        #[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        // ==================================================
537        // work_count
538
539        #[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    // ============================================================
550    // LowRateDecoder
551
552    mod low_rate_decoder {
553        use crate::{
554            engine::NoSimd,
555            rate::{LowRateDecoder, RateDecoder},
556            Error,
557        };
558
559        // ==================================================
560        // ERRORS
561
562        test_rate_decoder_errors! {LowRateDecoder}
563
564        // ==================================================
565        // supports
566
567        #[test]
568        fn supports() {
569            assert!(LowRateDecoder::<NoSimd>::supports(4096, 61440));
570            assert!(!LowRateDecoder::<NoSimd>::supports(61440, 4096));
571        }
572
573        // ==================================================
574        // validate
575
576        #[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        // ==================================================
595        // work_count
596
597        #[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}