1use crate::reed_solomon::{
2 engine::{Engine, GF_ORDER},
3 rate::{
4 DecoderWork, EncoderWork, HighRate, HighRateDecoder, HighRateEncoder, LowRate,
5 LowRateDecoder, LowRateEncoder, Rate, RateDecoder, RateEncoder,
6 },
7 DecoderResult, EncoderResult, Error,
8};
9use core::{cmp::Ordering, marker::PhantomData};
10
11fn use_high_rate(original_count: usize, recovery_count: usize) -> Result<bool, Error> {
15 if original_count > GF_ORDER || recovery_count > GF_ORDER {
16 return Err(Error::UnsupportedShardCount {
17 original_count,
18 recovery_count,
19 });
20 }
21
22 let original_count_pow2 = original_count.next_power_of_two();
23 let recovery_count_pow2 = recovery_count.next_power_of_two();
24
25 let smaller_pow2 = core::cmp::min(original_count_pow2, recovery_count_pow2);
26 let larger = core::cmp::max(original_count, recovery_count);
27
28 if original_count == 0 || recovery_count == 0 || smaller_pow2 + larger > GF_ORDER {
29 return Err(Error::UnsupportedShardCount {
30 original_count,
31 recovery_count,
32 });
33 }
34
35 match original_count_pow2.cmp(&recovery_count_pow2) {
36 Ordering::Less => {
37 Ok(false)
41 }
42
43 Ordering::Greater => {
44 Ok(true)
48 }
49
50 Ordering::Equal => {
51 if original_count <= recovery_count {
55 Ok(true)
57 } else {
58 Ok(false)
60 }
61 }
62 }
63}
64
65fn validate_rate<E: Engine>(
66 original_count: usize,
67 recovery_count: usize,
68 shard_bytes: usize,
69) -> Result<bool, Error> {
70 let use_high = use_high_rate(original_count, recovery_count)?;
71 if use_high {
72 HighRate::<E>::validate(original_count, recovery_count, shard_bytes)?;
73 } else {
74 LowRate::<E>::validate(original_count, recovery_count, shard_bytes)?;
75 }
76 Ok(use_high)
77}
78
79pub struct DefaultRate<E: Engine>(PhantomData<E>);
84
85impl<E: Engine> Rate<E> for DefaultRate<E> {
86 type RateEncoder = DefaultRateEncoder<E>;
87 type RateDecoder = DefaultRateDecoder<E>;
88
89 fn supports(original_count: usize, recovery_count: usize) -> bool {
90 use_high_rate(original_count, recovery_count).is_ok()
91 }
92}
93
94#[derive(Default)]
98enum InnerEncoder<E: Engine> {
99 High(HighRateEncoder<E>),
100 Low(LowRateEncoder<E>),
101
102 #[default]
104 None,
105}
106
107pub struct DefaultRateEncoder<E: Engine>(InnerEncoder<E>);
118
119impl<E: Engine> RateEncoder<E> for DefaultRateEncoder<E> {
120 type Rate = DefaultRate<E>;
121
122 fn add_original_shard<T: AsRef<[u8]>>(&mut self, original_shard: T) -> Result<(), Error> {
123 match &mut self.0 {
124 InnerEncoder::High(high) => high.add_original_shard(original_shard),
125 InnerEncoder::Low(low) => low.add_original_shard(original_shard),
126 InnerEncoder::None => unreachable!(),
127 }
128 }
129
130 fn encode(&mut self) -> Result<EncoderResult<'_>, Error> {
131 match &mut self.0 {
132 InnerEncoder::High(high) => high.encode(),
133 InnerEncoder::Low(low) => low.encode(),
134 InnerEncoder::None => unreachable!(),
135 }
136 }
137
138 fn into_parts(self) -> (E, EncoderWork) {
139 match self.0 {
140 InnerEncoder::High(high) => high.into_parts(),
141 InnerEncoder::Low(low) => low.into_parts(),
142 InnerEncoder::None => unreachable!(),
143 }
144 }
145
146 fn new(
147 original_count: usize,
148 recovery_count: usize,
149 shard_bytes: usize,
150 engine: E,
151 work: Option<EncoderWork>,
152 ) -> Result<Self, Error> {
153 let inner = if use_high_rate(original_count, recovery_count)? {
154 InnerEncoder::High(HighRateEncoder::new(
155 original_count,
156 recovery_count,
157 shard_bytes,
158 engine,
159 work,
160 )?)
161 } else {
162 InnerEncoder::Low(LowRateEncoder::new(
163 original_count,
164 recovery_count,
165 shard_bytes,
166 engine,
167 work,
168 )?)
169 };
170
171 Ok(Self(inner))
172 }
173
174 fn reset(
175 &mut self,
176 original_count: usize,
177 recovery_count: usize,
178 shard_bytes: usize,
179 ) -> Result<(), Error> {
180 let new_rate_is_high = validate_rate::<E>(original_count, recovery_count, shard_bytes)?;
181
182 match &mut self.0 {
183 InnerEncoder::High(high) if new_rate_is_high => {
184 return high.reset(original_count, recovery_count, shard_bytes);
185 }
186 InnerEncoder::Low(low) if !new_rate_is_high => {
187 return low.reset(original_count, recovery_count, shard_bytes);
188 }
189 _ => {}
190 }
191
192 self.0 = match core::mem::take(&mut self.0) {
193 InnerEncoder::High(high) => {
194 let (engine, work) = high.into_parts();
195 InnerEncoder::Low(
196 LowRateEncoder::new(
197 original_count,
198 recovery_count,
199 shard_bytes,
200 engine,
201 Some(work),
202 )
203 .expect("low-rate encoder configuration was validated"),
204 )
205 }
206
207 InnerEncoder::Low(low) => {
208 let (engine, work) = low.into_parts();
209 InnerEncoder::High(
210 HighRateEncoder::new(
211 original_count,
212 recovery_count,
213 shard_bytes,
214 engine,
215 Some(work),
216 )
217 .expect("high-rate encoder configuration was validated"),
218 )
219 }
220
221 InnerEncoder::None => unreachable!(),
222 };
223
224 Ok(())
225 }
226}
227
228#[derive(Default)]
232enum InnerDecoder<E: Engine> {
233 High(HighRateDecoder<E>),
234 Low(LowRateDecoder<E>),
235
236 #[default]
238 None,
239}
240
241pub struct DefaultRateDecoder<E: Engine>(InnerDecoder<E>);
252
253impl<E: Engine> RateDecoder<E> for DefaultRateDecoder<E> {
254 type Rate = DefaultRate<E>;
255
256 fn add_original_shard<T: AsRef<[u8]>>(
257 &mut self,
258 index: usize,
259 original_shard: T,
260 ) -> Result<(), Error> {
261 match &mut self.0 {
262 InnerDecoder::High(high) => high.add_original_shard(index, original_shard),
263 InnerDecoder::Low(low) => low.add_original_shard(index, original_shard),
264 InnerDecoder::None => unreachable!(),
265 }
266 }
267
268 fn add_recovery_shard<T: AsRef<[u8]>>(
269 &mut self,
270 index: usize,
271 recovery_shard: T,
272 ) -> Result<(), Error> {
273 match &mut self.0 {
274 InnerDecoder::High(high) => high.add_recovery_shard(index, recovery_shard),
275 InnerDecoder::Low(low) => low.add_recovery_shard(index, recovery_shard),
276 InnerDecoder::None => unreachable!(),
277 }
278 }
279
280 fn decode(&mut self, compute_recovery: bool) -> Result<Option<DecoderResult<'_>>, Error> {
281 match &mut self.0 {
282 InnerDecoder::High(high) => high.decode(compute_recovery),
283 InnerDecoder::Low(low) => low.decode(compute_recovery),
284 InnerDecoder::None => unreachable!(),
285 }
286 }
287
288 fn into_parts(self) -> (E, DecoderWork) {
289 match self.0 {
290 InnerDecoder::High(high) => high.into_parts(),
291 InnerDecoder::Low(low) => low.into_parts(),
292 InnerDecoder::None => unreachable!(),
293 }
294 }
295
296 fn new(
297 original_count: usize,
298 recovery_count: usize,
299 shard_bytes: usize,
300 engine: E,
301 work: Option<DecoderWork>,
302 ) -> Result<Self, Error> {
303 let inner = if use_high_rate(original_count, recovery_count)? {
304 InnerDecoder::High(HighRateDecoder::new(
305 original_count,
306 recovery_count,
307 shard_bytes,
308 engine,
309 work,
310 )?)
311 } else {
312 InnerDecoder::Low(LowRateDecoder::new(
313 original_count,
314 recovery_count,
315 shard_bytes,
316 engine,
317 work,
318 )?)
319 };
320
321 Ok(Self(inner))
322 }
323
324 fn reset(
325 &mut self,
326 original_count: usize,
327 recovery_count: usize,
328 shard_bytes: usize,
329 ) -> Result<(), Error> {
330 let new_rate_is_high = validate_rate::<E>(original_count, recovery_count, shard_bytes)?;
331
332 match &mut self.0 {
333 InnerDecoder::High(high) if new_rate_is_high => {
334 return high.reset(original_count, recovery_count, shard_bytes);
335 }
336 InnerDecoder::Low(low) if !new_rate_is_high => {
337 return low.reset(original_count, recovery_count, shard_bytes);
338 }
339 _ => {}
340 }
341
342 self.0 = match core::mem::take(&mut self.0) {
343 InnerDecoder::High(high) => {
344 let (engine, work) = high.into_parts();
345 InnerDecoder::Low(
346 LowRateDecoder::new(
347 original_count,
348 recovery_count,
349 shard_bytes,
350 engine,
351 Some(work),
352 )
353 .expect("low-rate decoder configuration was validated"),
354 )
355 }
356
357 InnerDecoder::Low(low) => {
358 let (engine, work) = low.into_parts();
359 InnerDecoder::High(
360 HighRateDecoder::new(
361 original_count,
362 recovery_count,
363 shard_bytes,
364 engine,
365 Some(work),
366 )
367 .expect("high-rate decoder configuration was validated"),
368 )
369 }
370
371 InnerDecoder::None => unreachable!(),
372 };
373
374 Ok(())
375 }
376}
377
378#[cfg(test)]
382mod tests {
383 use super::*;
384 use crate::reed_solomon::test_util;
385
386 #[test]
390 fn roundtrips_tiny() {
391 for (original_count, recovery_count, seed, recovery_hash) in test_util::DEFAULT_TINY {
392 roundtrip_single!(
393 DefaultRate,
394 *original_count,
395 *recovery_count,
396 1024,
397 recovery_hash,
398 &[test_util::range(*recovery_count, *original_count)],
399 &[test_util::range(
400 0,
401 core::cmp::min(*original_count, *recovery_count)
402 )],
403 *seed,
404 );
405 }
406 }
407
408 #[test]
412 fn two_rounds_implicit_reset() {
413 roundtrip_two_rounds!(
414 DefaultRate,
415 false,
416 (
417 2,
418 3,
419 1024,
420 test_util::LOW_2_3,
421 &[],
422 &[test_util::index(0), test_util::index(2)],
423 123
424 ),
425 (
426 2,
427 3,
428 1024,
429 test_util::LOW_2_3_223,
430 &[test_util::index(0)],
431 &[test_util::index(1)],
432 223
433 ),
434 );
435 }
436
437 #[test]
438 fn two_rounds_reset_high_to_high() {
439 roundtrip_two_rounds!(
440 DefaultRate,
441 true,
442 (
443 3,
444 2,
445 1024,
446 test_util::HIGH_3_2,
447 &[test_util::index(1)],
448 &[test_util::index(0), test_util::index(1)],
449 132
450 ),
451 (
452 5,
453 3,
454 1024,
455 test_util::HIGH_5_3,
456 &[test_util::index(1), test_util::index(3)],
457 &[
458 test_util::index(0),
459 test_util::index(1),
460 test_util::index(2)
461 ],
462 153
463 ),
464 );
465 }
466
467 #[test]
468 fn two_rounds_reset_high_to_low() {
469 roundtrip_two_rounds!(
470 DefaultRate,
471 true,
472 (
473 3,
474 2,
475 1024,
476 test_util::HIGH_3_2,
477 &[test_util::index(1)],
478 &[test_util::index(0), test_util::index(1)],
479 132
480 ),
481 (
482 2,
483 3,
484 1024,
485 test_util::LOW_2_3,
486 &[],
487 &[test_util::index(0), test_util::index(2)],
488 123
489 ),
490 );
491 }
492
493 #[test]
494 fn two_rounds_reset_low_to_high() {
495 roundtrip_two_rounds!(
496 DefaultRate,
497 true,
498 (
499 2,
500 3,
501 1024,
502 test_util::LOW_2_3,
503 &[],
504 &[test_util::index(0), test_util::index(1)],
505 123
506 ),
507 (
508 3,
509 2,
510 1024,
511 test_util::HIGH_3_2,
512 &[test_util::index(1)],
513 &[test_util::index(0), test_util::index(1)],
514 132
515 ),
516 );
517 }
518
519 #[test]
520 fn two_rounds_reset_low_to_low() {
521 roundtrip_two_rounds!(
522 DefaultRate,
523 true,
524 (
525 2,
526 3,
527 1024,
528 test_util::LOW_2_3,
529 &[],
530 &[test_util::index(0), test_util::index(2)],
531 123
532 ),
533 (
534 3,
535 5,
536 1024,
537 test_util::LOW_3_5,
538 &[],
539 &[
540 test_util::index(0),
541 test_util::index(2),
542 test_util::index(4)
543 ],
544 135
545 ),
546 );
547 }
548
549 #[test]
553 fn use_high_rate() {
554 fn err(original_count: usize, recovery_count: usize) -> Result<bool, Error> {
555 Err(Error::UnsupportedShardCount {
556 original_count,
557 recovery_count,
558 })
559 }
560
561 for (original_count, recovery_count, expected) in [
562 (0, 1, err(0, 1)),
563 (1, 0, err(1, 0)),
564 (3, 3, Ok(true)),
566 (3, 4, Ok(true)),
567 (3, 5, Ok(false)),
568 (4, 3, Ok(false)),
569 (5, 3, Ok(true)),
570 (4096, 61440, Ok(false)),
572 (4096, 61441, err(4096, 61441)),
573 (4097, 61440, err(4097, 61440)),
574 (61440, 4096, Ok(true)),
576 (61440, 4097, err(61440, 4097)),
577 (61441, 4096, err(61441, 4096)),
578 (usize::MAX, usize::MAX, err(usize::MAX, usize::MAX)),
580 ] {
581 assert_eq!(
582 super::use_high_rate(original_count, recovery_count),
583 expected
584 );
585 }
586 }
587}