1use crate::reed_solomon::{
2 DecoderResult, EncoderResult, Error, Plan,
3 engine::{Engine, GF_ORDER},
4 rate::{
5 DecoderWork, EncoderWork, HighRateDecoder, HighRateEncoder, LowRateDecoder, LowRateEncoder,
6 Rate, RateDecoder, RateEncoder,
7 },
8};
9use core::{cmp::Ordering, marker::PhantomData};
10
11pub(crate) fn use_high_rate(original_count: usize, recovery_count: usize) -> Result<bool, Error> {
20 if original_count > GF_ORDER || recovery_count > GF_ORDER {
21 return Err(Error::UnsupportedShardCount {
22 original_count,
23 recovery_count,
24 });
25 }
26
27 let original_count_pow2 = original_count.next_power_of_two();
28 let recovery_count_pow2 = recovery_count.next_power_of_two();
29
30 let smaller_pow2 = core::cmp::min(original_count_pow2, recovery_count_pow2);
31 let larger = core::cmp::max(original_count, recovery_count);
32
33 if original_count == 0 || recovery_count == 0 || smaller_pow2 + larger > GF_ORDER {
34 return Err(Error::UnsupportedShardCount {
35 original_count,
36 recovery_count,
37 });
38 }
39
40 match original_count_pow2.cmp(&recovery_count_pow2) {
41 Ordering::Less => {
42 Ok(false)
45 }
46
47 Ordering::Greater => {
48 Ok(true)
51 }
52
53 Ordering::Equal => {
54 if original_count <= recovery_count {
57 Ok(true)
59 } else {
60 Ok(false)
62 }
63 }
64 }
65}
66
67pub struct DefaultRate<E: Engine>(PhantomData<E>);
69
70impl<E: Engine> Rate<E> for DefaultRate<E> {
71 type RateEncoder = DefaultRateEncoder<E>;
72 type RateDecoder = DefaultRateDecoder<E>;
73
74 fn supports(original_count: usize, recovery_count: usize) -> bool {
75 use_high_rate(original_count, recovery_count).is_ok()
76 }
77}
78
79#[derive(Default)]
81enum InnerEncoder<E: Engine> {
82 High(HighRateEncoder<E>),
83 Low(LowRateEncoder<E>),
84
85 #[default]
87 None,
88}
89
90pub struct DefaultRateEncoder<E: Engine>(InnerEncoder<E>);
98
99impl<E: Engine> RateEncoder<E> for DefaultRateEncoder<E> {
100 type Rate = DefaultRate<E>;
101
102 fn validate(
103 original_count: usize,
104 recovery_count: usize,
105 shard_bytes: usize,
106 ) -> Result<(), Error> {
107 if use_high_rate(original_count, recovery_count)? {
108 HighRateEncoder::<E>::validate(original_count, recovery_count, shard_bytes)
109 } else {
110 LowRateEncoder::<E>::validate(original_count, recovery_count, shard_bytes)
111 }
112 }
113
114 fn add_original_shard<T: AsRef<[u8]>>(&mut self, original_shard: T) -> Result<(), Error> {
115 match &mut self.0 {
116 InnerEncoder::High(high) => high.add_original_shard(original_shard),
117 InnerEncoder::Low(low) => low.add_original_shard(original_shard),
118 InnerEncoder::None => unreachable!(),
119 }
120 }
121
122 fn encode(&mut self) -> Result<EncoderResult<'_>, Error> {
123 match &mut self.0 {
124 InnerEncoder::High(high) => high.encode(),
125 InnerEncoder::Low(low) => low.encode(),
126 InnerEncoder::None => unreachable!(),
127 }
128 }
129
130 fn into_parts(self) -> (E, EncoderWork) {
131 match self.0 {
132 InnerEncoder::High(high) => high.into_parts(),
133 InnerEncoder::Low(low) => low.into_parts(),
134 InnerEncoder::None => unreachable!(),
135 }
136 }
137
138 fn new(
139 original_count: usize,
140 recovery_count: usize,
141 shard_bytes: usize,
142 engine: E,
143 work: Option<EncoderWork>,
144 ) -> Result<Self, Error> {
145 let inner = if use_high_rate(original_count, recovery_count)? {
146 InnerEncoder::High(HighRateEncoder::new(
147 original_count,
148 recovery_count,
149 shard_bytes,
150 engine,
151 work,
152 )?)
153 } else {
154 InnerEncoder::Low(LowRateEncoder::new(
155 original_count,
156 recovery_count,
157 shard_bytes,
158 engine,
159 work,
160 )?)
161 };
162
163 Ok(Self(inner))
164 }
165
166 fn reset(
167 &mut self,
168 original_count: usize,
169 recovery_count: usize,
170 shard_bytes: usize,
171 ) -> Result<(), Error> {
172 Self::validate(original_count, recovery_count, shard_bytes)?;
173 let new_rate_is_high = use_high_rate(original_count, recovery_count)?;
174
175 match &mut self.0 {
176 InnerEncoder::High(high) if new_rate_is_high => {
177 return high.reset(original_count, recovery_count, shard_bytes);
178 }
179 InnerEncoder::Low(low) if !new_rate_is_high => {
180 return low.reset(original_count, recovery_count, shard_bytes);
181 }
182 _ => {}
183 }
184
185 self.0 = match core::mem::take(&mut self.0) {
186 InnerEncoder::High(high) => {
187 let (engine, work) = high.into_parts();
188 InnerEncoder::Low(
189 LowRateEncoder::new(
190 original_count,
191 recovery_count,
192 shard_bytes,
193 engine,
194 Some(work),
195 )
196 .expect("low-rate encoder configuration was validated"),
197 )
198 }
199
200 InnerEncoder::Low(low) => {
201 let (engine, work) = low.into_parts();
202 InnerEncoder::High(
203 HighRateEncoder::new(
204 original_count,
205 recovery_count,
206 shard_bytes,
207 engine,
208 Some(work),
209 )
210 .expect("high-rate encoder configuration was validated"),
211 )
212 }
213
214 InnerEncoder::None => unreachable!(),
215 };
216
217 Ok(())
218 }
219}
220
221#[derive(Default)]
223enum InnerDecoder<E: Engine> {
224 High(HighRateDecoder<E>),
225 Low(LowRateDecoder<E>),
226
227 #[default]
229 None,
230}
231
232pub struct DefaultRateDecoder<E: Engine>(InnerDecoder<E>);
240
241impl<E: Engine> RateDecoder<E> for DefaultRateDecoder<E> {
242 type Rate = DefaultRate<E>;
243
244 fn validate(
245 original_count: usize,
246 recovery_count: usize,
247 shard_bytes: usize,
248 ) -> Result<(), Error> {
249 if use_high_rate(original_count, recovery_count)? {
250 HighRateDecoder::<E>::validate(original_count, recovery_count, shard_bytes)
251 } else {
252 LowRateDecoder::<E>::validate(original_count, recovery_count, shard_bytes)
253 }
254 }
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 Self::validate(original_count, recovery_count, shard_bytes)?;
331 let new_rate_is_high = use_high_rate(original_count, recovery_count)?;
332
333 match &mut self.0 {
334 InnerDecoder::High(high) if new_rate_is_high => {
335 return high.reset(original_count, recovery_count, shard_bytes);
336 }
337 InnerDecoder::Low(low) if !new_rate_is_high => {
338 return low.reset(original_count, recovery_count, shard_bytes);
339 }
340 _ => {}
341 }
342
343 self.0 = match core::mem::take(&mut self.0) {
344 InnerDecoder::High(high) => {
345 let (engine, work) = high.into_parts();
346 InnerDecoder::Low(
347 LowRateDecoder::new(
348 original_count,
349 recovery_count,
350 shard_bytes,
351 engine,
352 Some(work),
353 )
354 .expect("low-rate decoder configuration was validated"),
355 )
356 }
357
358 InnerDecoder::Low(low) => {
359 let (engine, work) = low.into_parts();
360 InnerDecoder::High(
361 HighRateDecoder::new(
362 original_count,
363 recovery_count,
364 shard_bytes,
365 engine,
366 Some(work),
367 )
368 .expect("high-rate decoder configuration was validated"),
369 )
370 }
371
372 InnerDecoder::None => unreachable!(),
373 };
374
375 Ok(())
376 }
377}
378
379impl<E: Engine> DefaultRateDecoder<E> {
380 pub(crate) fn decode_with_plan(
386 &mut self,
387 compute_recovery: bool,
388 plan: &Plan,
389 ) -> Result<Option<DecoderResult<'_>>, Error> {
390 match &mut self.0 {
391 InnerDecoder::High(high) => high.decode_with_plan(compute_recovery, plan),
392 InnerDecoder::Low(low) => low.decode_with_plan(compute_recovery, plan),
393 InnerDecoder::None => unreachable!(),
394 }
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use crate::reed_solomon::test_util;
402
403 #[test]
404 fn roundtrips_tiny() {
405 for (original_count, recovery_count, seed, recovery_hash) in test_util::DEFAULT_TINY {
406 roundtrip_single!(
407 DefaultRate,
408 *original_count,
409 *recovery_count,
410 1024,
411 recovery_hash,
412 &[test_util::range(*recovery_count, *original_count)],
413 &[test_util::range(
414 0,
415 core::cmp::min(*original_count, *recovery_count)
416 )],
417 *seed,
418 );
419 }
420 }
421
422 #[test]
423 fn two_rounds_implicit_reset() {
424 roundtrip_two_rounds!(
425 DefaultRate,
426 false,
427 (
428 2,
429 3,
430 1024,
431 test_util::LOW_2_3,
432 &[],
433 &[test_util::index(0), test_util::index(2)],
434 123
435 ),
436 (
437 2,
438 3,
439 1024,
440 test_util::LOW_2_3_223,
441 &[test_util::index(0)],
442 &[test_util::index(1)],
443 223
444 ),
445 );
446 }
447
448 #[test]
449 fn two_rounds_reset_high_to_high() {
450 roundtrip_two_rounds!(
451 DefaultRate,
452 true,
453 (
454 3,
455 2,
456 1024,
457 test_util::HIGH_3_2,
458 &[test_util::index(1)],
459 &[test_util::index(0), test_util::index(1)],
460 132
461 ),
462 (
463 5,
464 3,
465 1024,
466 test_util::HIGH_5_3,
467 &[test_util::index(1), test_util::index(3)],
468 &[
469 test_util::index(0),
470 test_util::index(1),
471 test_util::index(2)
472 ],
473 153
474 ),
475 );
476 }
477
478 #[test]
479 fn two_rounds_reset_high_to_low() {
480 roundtrip_two_rounds!(
481 DefaultRate,
482 true,
483 (
484 3,
485 2,
486 1024,
487 test_util::HIGH_3_2,
488 &[test_util::index(1)],
489 &[test_util::index(0), test_util::index(1)],
490 132
491 ),
492 (
493 2,
494 3,
495 1024,
496 test_util::LOW_2_3,
497 &[],
498 &[test_util::index(0), test_util::index(2)],
499 123
500 ),
501 );
502 }
503
504 #[test]
505 fn two_rounds_reset_low_to_high() {
506 roundtrip_two_rounds!(
507 DefaultRate,
508 true,
509 (
510 2,
511 3,
512 1024,
513 test_util::LOW_2_3,
514 &[],
515 &[test_util::index(0), test_util::index(1)],
516 123
517 ),
518 (
519 3,
520 2,
521 1024,
522 test_util::HIGH_3_2,
523 &[test_util::index(1)],
524 &[test_util::index(0), test_util::index(1)],
525 132
526 ),
527 );
528 }
529
530 #[test]
531 fn two_rounds_reset_low_to_low() {
532 roundtrip_two_rounds!(
533 DefaultRate,
534 true,
535 (
536 2,
537 3,
538 1024,
539 test_util::LOW_2_3,
540 &[],
541 &[test_util::index(0), test_util::index(2)],
542 123
543 ),
544 (
545 3,
546 5,
547 1024,
548 test_util::LOW_3_5,
549 &[],
550 &[
551 test_util::index(0),
552 test_util::index(2),
553 test_util::index(4)
554 ],
555 135
556 ),
557 );
558 }
559
560 #[test]
561 fn use_high_rate() {
562 fn err(original_count: usize, recovery_count: usize) -> Result<bool, Error> {
563 Err(Error::UnsupportedShardCount {
564 original_count,
565 recovery_count,
566 })
567 }
568
569 for (original_count, recovery_count, expected) in [
570 (0, 1, err(0, 1)),
571 (1, 0, err(1, 0)),
572 (3, 3, Ok(true)),
574 (3, 4, Ok(true)),
575 (3, 5, Ok(false)),
576 (4, 3, Ok(false)),
577 (5, 3, Ok(true)),
578 (4096, 61440, Ok(false)),
580 (4096, 61441, err(4096, 61441)),
581 (4097, 61440, err(4097, 61440)),
582 (61440, 4096, Ok(true)),
584 (61440, 4097, err(61440, 4097)),
585 (61441, 4096, err(61441, 4096)),
586 (usize::MAX, usize::MAX, err(usize::MAX, usize::MAX)),
588 ] {
589 assert_eq!(
590 super::use_high_rate(original_count, recovery_count),
591 expected
592 );
593 }
594 }
595}