1#![expect(incomplete_features, reason = "generic_const_*")]
2#![feature(
3 gca_const_items,
4 gca_macroless_args,
5 gca_min_const_items,
6 generic_const_items
7)]
8#![no_std]
9
10use core::{array, fmt, slice};
11use reed_solomon_simd::Error;
12use reed_solomon_simd::engine::DefaultEngine;
13use reed_solomon_simd::rate::{HighRateDecoder, HighRateEncoder, RateDecoder, RateEncoder};
14
15#[derive(Debug, Clone, PartialEq, thiserror::Error)]
17pub enum ErasureCodingError {
18 #[error("Decoder error: {0}")]
20 DecoderError(#[from] Error),
21}
22
23const BLOCK_BYTES: usize = 64;
25const TARGET_COLUMNS_BYTES: usize = 256 * 1024;
30const MIN_COLUMN_BYTES: usize = 1024;
32#[cfg(feature = "parallel")]
37const MAX_PARALLELISM: usize = 4;
38
39const NUM_WORDS<const NUM_SHARDS: usize>: usize = NUM_SHARDS.div_ceil(u64::BITS as usize);
41
42#[derive(Copy, Clone, Eq, PartialEq)]
47pub struct ShardsBitmap<const NUM_SHARDS: usize> {
48 words: [u64; NUM_WORDS::<NUM_SHARDS>],
49}
50
51impl<const NUM_SHARDS: usize> fmt::Debug for ShardsBitmap<NUM_SHARDS> {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 f.debug_struct("ShardsBitmap")
54 .field("num_shards", &NUM_SHARDS)
55 .field("num_set", &self.count())
56 .finish()
57 }
58}
59
60impl<const NUM_SHARDS: usize> Default for ShardsBitmap<NUM_SHARDS> {
61 #[inline(always)]
62 fn default() -> Self {
63 Self::none()
64 }
65}
66
67impl<const NUM_SHARDS: usize> ShardsBitmap<NUM_SHARDS> {
68 #[inline(always)]
70 pub const fn none() -> Self {
71 Self {
72 words: [0; NUM_WORDS::<NUM_SHARDS>],
73 }
74 }
75
76 #[inline(always)]
78 pub const fn all() -> Self {
79 let mut this = Self {
80 words: [u64::MAX; NUM_WORDS::<NUM_SHARDS>],
81 };
82
83 let unused_bits = NUM_WORDS::<NUM_SHARDS> * u64::BITS as usize - NUM_SHARDS;
85 if unused_bits > 0 {
86 this.words[NUM_WORDS::<NUM_SHARDS> - 1] >>= unused_bits;
87 }
88
89 this
90 }
91
92 #[inline(always)]
94 pub const fn get(&self, index: usize) -> bool {
95 if index >= NUM_SHARDS {
96 return false;
97 }
98
99 (self.words[index / u64::BITS as usize] >> (index % u64::BITS as usize)) & 1 == 1
100 }
101
102 #[inline(always)]
104 pub const fn set(&mut self, index: usize) {
105 if index < NUM_SHARDS {
106 self.words[index / u64::BITS as usize] |= 1 << (index % u64::BITS as usize);
107 }
108 }
109
110 #[inline(always)]
112 pub const fn unset(&mut self, index: usize) {
113 if index < NUM_SHARDS {
114 self.words[index / u64::BITS as usize] &= !(1 << (index % u64::BITS as usize));
115 }
116 }
117
118 #[inline]
120 pub const fn count(&self) -> usize {
121 let mut count = 0;
122 let mut word_index = 0;
123 while word_index < NUM_WORDS::<NUM_SHARDS> {
124 count += self.words[word_index].count_ones() as usize;
125 word_index += 1;
126 }
127
128 count
129 }
130}
131
132#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
134pub struct ShardsPresent<const NUM_SHARDS: usize> {
135 pub source: ShardsBitmap<NUM_SHARDS>,
137 pub parity: ShardsBitmap<NUM_SHARDS>,
139}
140
141impl<const NUM_SHARDS: usize> ShardsPresent<NUM_SHARDS> {
142 #[inline(always)]
144 pub const fn none() -> Self {
145 Self {
146 source: ShardsBitmap::none(),
147 parity: ShardsBitmap::none(),
148 }
149 }
150
151 #[inline(always)]
153 pub const fn all() -> Self {
154 Self {
155 source: ShardsBitmap::all(),
156 parity: ShardsBitmap::all(),
157 }
158 }
159}
160
161#[derive(Debug, Clone)]
170pub struct ErasureCoding;
171
172impl Default for ErasureCoding {
173 fn default() -> Self {
174 Self::new()
175 }
176}
177
178impl ErasureCoding {
179 pub fn new() -> Self {
181 Self {}
182 }
183
184 #[inline]
186 pub fn extend<const NUM_SHARDS: usize, const SHARD_BYTES: usize>(
187 &self,
188 source: &[[u8; SHARD_BYTES]; NUM_SHARDS],
189 parity: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
190 ) -> Result<(), ErasureCodingError> {
191 Ok(extend::<NUM_SHARDS, SHARD_BYTES, false, _, _>(
192 source, parity,
193 )?)
194 }
195
196 #[cfg(feature = "parallel")]
205 #[inline]
206 pub fn par_extend<const NUM_SHARDS: usize, const SHARD_BYTES: usize>(
207 &self,
208 source: &[[u8; SHARD_BYTES]; NUM_SHARDS],
209 parity: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
210 ) -> Result<(), ErasureCodingError> {
211 Ok(extend::<NUM_SHARDS, SHARD_BYTES, true, _, _>(
212 source, parity,
213 )?)
214 }
215
216 #[inline]
218 pub fn extend_scattered<
219 const NUM_SHARDS: usize,
220 const SHARD_BYTES: usize,
221 SourceShard,
222 ParityShard,
223 >(
224 &self,
225 source: [SourceShard; NUM_SHARDS],
226 parity: [ParityShard; NUM_SHARDS],
227 ) -> Result<(), ErasureCodingError>
228 where
229 SourceShard: AsRef<[u8; SHARD_BYTES]>,
230 ParityShard: AsMut<[u8; SHARD_BYTES]>,
231 {
232 let mut parity = parity;
233
234 Ok(extend::<NUM_SHARDS, SHARD_BYTES, false, _, _>(
235 &source.each_ref().map(|shard| shard.as_ref().as_slice()),
236 &mut parity.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
237 )?)
238 }
239
240 #[cfg(feature = "parallel")]
249 #[inline]
250 pub fn par_extend_scattered<
251 const NUM_SHARDS: usize,
252 const SHARD_BYTES: usize,
253 SourceShard,
254 ParityShard,
255 >(
256 &self,
257 source: [SourceShard; NUM_SHARDS],
258 parity: [ParityShard; NUM_SHARDS],
259 ) -> Result<(), ErasureCodingError>
260 where
261 SourceShard: AsRef<[u8; SHARD_BYTES]>,
262 ParityShard: AsMut<[u8; SHARD_BYTES]>,
263 {
264 let mut parity = parity;
265
266 Ok(extend::<NUM_SHARDS, SHARD_BYTES, true, _, _>(
267 &source.each_ref().map(|shard| shard.as_ref().as_slice()),
268 &mut parity.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
269 )?)
270 }
271
272 #[inline]
277 pub fn recover_source<const NUM_SHARDS: usize, const SHARD_BYTES: usize>(
278 &self,
279 source: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
280 parity: &[[u8; SHARD_BYTES]; NUM_SHARDS],
281 present: &ShardsPresent<NUM_SHARDS>,
282 ) -> Result<(), ErasureCodingError> {
283 Ok(recover_source::<NUM_SHARDS, SHARD_BYTES, false, _, _>(
284 source,
285 &present.source,
286 parity
287 .iter()
288 .enumerate()
289 .map(|(index, shard)| present.parity.get(index).then_some(shard.as_slice())),
290 )?)
291 }
292
293 #[cfg(feature = "parallel")]
302 #[inline]
303 pub fn par_recover_source<const NUM_SHARDS: usize, const SHARD_BYTES: usize>(
304 &self,
305 source: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
306 parity: &[[u8; SHARD_BYTES]; NUM_SHARDS],
307 present: &ShardsPresent<NUM_SHARDS>,
308 ) -> Result<(), ErasureCodingError> {
309 Ok(recover_source::<NUM_SHARDS, SHARD_BYTES, true, _, _>(
310 source,
311 &present.source,
312 parity
313 .iter()
314 .enumerate()
315 .map(|(index, shard)| present.parity.get(index).then_some(shard.as_slice())),
316 )?)
317 }
318
319 #[inline]
323 pub fn recover_source_scattered<
324 const NUM_SHARDS: usize,
325 const SHARD_BYTES: usize,
326 SourceShard,
327 ParityShard,
328 >(
329 &self,
330 source: [SourceShard; NUM_SHARDS],
331 source_present: &ShardsBitmap<NUM_SHARDS>,
332 parity: [Option<ParityShard>; NUM_SHARDS],
333 ) -> Result<(), ErasureCodingError>
334 where
335 SourceShard: AsMut<[u8; SHARD_BYTES]>,
336 ParityShard: AsRef<[u8; SHARD_BYTES]>,
337 {
338 let mut source = source;
339
340 Ok(recover_source::<NUM_SHARDS, SHARD_BYTES, false, _, _>(
341 &mut source.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
342 source_present,
343 parity
344 .iter()
345 .map(|maybe_shard| maybe_shard.as_ref().map(|shard| shard.as_ref().as_slice())),
346 )?)
347 }
348
349 #[cfg(feature = "parallel")]
358 #[inline]
359 pub fn par_recover_source_scattered<
360 const NUM_SHARDS: usize,
361 const SHARD_BYTES: usize,
362 SourceShard,
363 ParityShard,
364 >(
365 &self,
366 source: [SourceShard; NUM_SHARDS],
367 source_present: &ShardsBitmap<NUM_SHARDS>,
368 parity: [Option<ParityShard>; NUM_SHARDS],
369 ) -> Result<(), ErasureCodingError>
370 where
371 SourceShard: AsMut<[u8; SHARD_BYTES]>,
372 ParityShard: AsRef<[u8; SHARD_BYTES]>,
373 {
374 let mut source = source;
375
376 Ok(recover_source::<NUM_SHARDS, SHARD_BYTES, true, _, _>(
377 &mut source.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
378 source_present,
379 parity
380 .iter()
381 .map(|maybe_shard| maybe_shard.as_ref().map(|shard| shard.as_ref().as_slice())),
382 )?)
383 }
384
385 #[inline]
389 pub fn recover_all<const NUM_SHARDS: usize, const SHARD_BYTES: usize>(
390 &self,
391 source: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
392 parity: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
393 present: &ShardsPresent<NUM_SHARDS>,
394 ) -> Result<(), ErasureCodingError> {
395 Ok(recover_all::<NUM_SHARDS, SHARD_BYTES, false, _, _>(
396 source, parity, present,
397 )?)
398 }
399
400 #[cfg(feature = "parallel")]
409 #[inline]
410 pub fn par_recover_all<const NUM_SHARDS: usize, const SHARD_BYTES: usize>(
411 &self,
412 source: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
413 parity: &mut [[u8; SHARD_BYTES]; NUM_SHARDS],
414 present: &ShardsPresent<NUM_SHARDS>,
415 ) -> Result<(), ErasureCodingError> {
416 Ok(recover_all::<NUM_SHARDS, SHARD_BYTES, true, _, _>(
417 source, parity, present,
418 )?)
419 }
420
421 #[inline]
423 pub fn recover_all_scattered<
424 const NUM_SHARDS: usize,
425 const SHARD_BYTES: usize,
426 SourceShard,
427 ParityShard,
428 >(
429 &self,
430 source: [SourceShard; NUM_SHARDS],
431 parity: [ParityShard; NUM_SHARDS],
432 present: &ShardsPresent<NUM_SHARDS>,
433 ) -> Result<(), ErasureCodingError>
434 where
435 SourceShard: AsMut<[u8; SHARD_BYTES]>,
436 ParityShard: AsMut<[u8; SHARD_BYTES]>,
437 {
438 let mut source = source;
439 let mut parity = parity;
440
441 Ok(recover_all::<NUM_SHARDS, SHARD_BYTES, false, _, _>(
442 &mut source.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
443 &mut parity.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
444 present,
445 )?)
446 }
447
448 #[cfg(feature = "parallel")]
457 #[inline]
458 pub fn par_recover_all_scattered<
459 const NUM_SHARDS: usize,
460 const SHARD_BYTES: usize,
461 SourceShard,
462 ParityShard,
463 >(
464 &self,
465 source: [SourceShard; NUM_SHARDS],
466 parity: [ParityShard; NUM_SHARDS],
467 present: &ShardsPresent<NUM_SHARDS>,
468 ) -> Result<(), ErasureCodingError>
469 where
470 SourceShard: AsMut<[u8; SHARD_BYTES]>,
471 ParityShard: AsMut<[u8; SHARD_BYTES]>,
472 {
473 let mut source = source;
474 let mut parity = parity;
475
476 Ok(recover_all::<NUM_SHARDS, SHARD_BYTES, true, _, _>(
477 &mut source.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
478 &mut parity.each_mut().map(|shard| shard.as_mut().as_mut_slice()),
479 present,
480 )?)
481 }
482}
483
484const fn column_bytes(num_shards: usize, shard_bytes: usize) -> usize {
491 let Some(column_bytes) = TARGET_COLUMNS_BYTES.checked_div(num_shards) else {
492 return shard_bytes;
493 };
494 let column_bytes = column_bytes / BLOCK_BYTES * BLOCK_BYTES;
495
496 if column_bytes < MIN_COLUMN_BYTES || column_bytes >= shard_bytes {
497 shard_bytes
498 } else {
499 column_bytes
500 }
501}
502
503fn extend<const NUM_SHARDS: usize, const SHARD_BYTES: usize, const PARALLEL: bool, Source, Parity>(
504 source: &[Source; NUM_SHARDS],
505 parity: &mut [Parity; NUM_SHARDS],
506) -> Result<(), Error>
507where
508 Source: AsRef<[u8]>,
509 Parity: AsMut<[u8]>,
510{
511 if const { column_bytes(NUM_SHARDS, SHARD_BYTES) == SHARD_BYTES } {
512 return encode(SHARD_BYTES, source, parity);
513 }
514
515 for_each_column::<NUM_SHARDS, PARALLEL, _, _, _>(
516 SHARD_BYTES,
517 const { column_bytes(NUM_SHARDS, SHARD_BYTES) },
518 source.each_ref().map(AsRef::as_ref),
519 parity.each_mut().map(AsMut::as_mut),
520 |shard_bytes, source, mut parity| encode(shard_bytes, &source, &mut parity),
521 )
522}
523
524fn recover_source<
526 'a,
527 const NUM_SHARDS: usize,
528 const SHARD_BYTES: usize,
529 const PARALLEL: bool,
530 Source,
531 Parity,
532>(
533 source: &mut [Source; NUM_SHARDS],
534 source_present: &ShardsBitmap<NUM_SHARDS>,
535 parity: Parity,
536) -> Result<(), Error>
537where
538 Source: AsRef<[u8]> + AsMut<[u8]>,
539 Parity: Iterator<Item = Option<&'a [u8]>>,
540{
541 if const { column_bytes(NUM_SHARDS, SHARD_BYTES) == SHARD_BYTES } {
542 return decode_source(SHARD_BYTES, source, source_present, parity);
543 }
544
545 let mut parity = parity;
546 let parity = array::from_fn::<_, NUM_SHARDS, _>(|_| parity.next().flatten());
547
548 for_each_column::<NUM_SHARDS, PARALLEL, _, _, _>(
549 SHARD_BYTES,
550 const { column_bytes(NUM_SHARDS, SHARD_BYTES) },
551 source.each_mut().map(AsMut::as_mut),
552 parity,
553 |shard_bytes, mut source, parity| {
554 decode_source(shard_bytes, &mut source, source_present, parity)
555 },
556 )
557}
558
559fn recover_all<
560 const NUM_SHARDS: usize,
561 const SHARD_BYTES: usize,
562 const PARALLEL: bool,
563 Source,
564 Parity,
565>(
566 source: &mut [Source; NUM_SHARDS],
567 parity: &mut [Parity; NUM_SHARDS],
568 present: &ShardsPresent<NUM_SHARDS>,
569) -> Result<(), Error>
570where
571 Source: AsRef<[u8]> + AsMut<[u8]>,
572 Parity: AsRef<[u8]> + AsMut<[u8]>,
573{
574 if const { column_bytes(NUM_SHARDS, SHARD_BYTES) == SHARD_BYTES } {
575 return decode_all(SHARD_BYTES, source, parity, present);
576 }
577
578 for_each_column::<NUM_SHARDS, PARALLEL, _, _, _>(
579 SHARD_BYTES,
580 const { column_bytes(NUM_SHARDS, SHARD_BYTES) },
581 source.each_mut().map(AsMut::as_mut),
582 parity.each_mut().map(AsMut::as_mut),
583 |shard_bytes, mut source, mut parity| {
584 decode_all(shard_bytes, &mut source, &mut parity, present)
585 },
586 )
587}
588
589trait IntoColumns: Sized {
591 type Columns: Iterator<Item = Self>;
593
594 fn into_columns(self, column_bytes: usize) -> Self::Columns;
595}
596
597impl<'a> IntoColumns for &'a [u8] {
598 type Columns = slice::Chunks<'a, u8>;
599
600 #[inline(always)]
601 fn into_columns(self, column_bytes: usize) -> Self::Columns {
602 self.chunks(column_bytes)
603 }
604}
605
606impl<'a> IntoColumns for &'a mut [u8] {
607 type Columns = slice::ChunksMut<'a, u8>;
608
609 #[inline(always)]
610 fn into_columns(self, column_bytes: usize) -> Self::Columns {
611 self.chunks_mut(column_bytes)
612 }
613}
614
615struct OptionalColumns<'a>(Option<slice::Chunks<'a, u8>>);
617
618impl<'a> Iterator for OptionalColumns<'a> {
619 type Item = Option<&'a [u8]>;
620
621 #[inline(always)]
622 fn next(&mut self) -> Option<Self::Item> {
623 Some(self.0.as_mut().and_then(Iterator::next))
624 }
625}
626
627impl<'a> IntoColumns for Option<&'a [u8]> {
628 type Columns = OptionalColumns<'a>;
629
630 #[inline(always)]
631 fn into_columns(self, column_bytes: usize) -> Self::Columns {
632 OptionalColumns(self.map(|shard| shard.chunks(column_bytes)))
633 }
634}
635
636fn columns<const NUM_SHARDS: usize, Shard>(
646 shard_bytes: usize,
647 column_bytes: usize,
648 shards: [Shard; NUM_SHARDS],
649) -> impl Iterator<Item = (usize, [Shard; NUM_SHARDS])>
650where
651 Shard: IntoColumns,
652{
653 let mut shards = shards.map(|shard| shard.into_columns(column_bytes));
654
655 (0..shard_bytes).step_by(column_bytes).map(move |offset| {
656 let column = shards.each_mut().map(|shard| {
657 shard
658 .next()
659 .expect("All shards have `shard_bytes` bytes and the same number of columns; qed")
660 });
661
662 (column_bytes.min(shard_bytes - offset), column)
663 })
664}
665
666fn for_each_column<const NUM_SHARDS: usize, const PARALLEL: bool, Source, Parity, Process>(
671 shard_bytes: usize,
672 column_bytes: usize,
673 source: [Source; NUM_SHARDS],
674 parity: [Parity; NUM_SHARDS],
675 process: Process,
676) -> Result<(), Error>
677where
678 Source: IntoColumns + Send,
679 Parity: IntoColumns + Send,
680 Process: Fn(usize, [Source; NUM_SHARDS], [Parity; NUM_SHARDS]) -> Result<(), Error> + Sync,
681{
682 #[cfg(feature = "parallel")]
683 if PARALLEL {
684 let num_columns = shard_bytes.div_ceil(column_bytes);
685 let region_bytes = num_columns.div_ceil(MAX_PARALLELISM) * column_bytes;
686 let process = &process;
687 let mut results = [const { Ok(()) }; MAX_PARALLELISM];
688
689 rayon::scope(|scope| {
690 let regions = columns(shard_bytes, region_bytes, source).zip(columns(
691 shard_bytes,
692 region_bytes,
693 parity,
694 ));
695
696 for (((region_bytes, source), (_, parity)), result) in regions.zip(&mut results) {
697 scope.spawn(move |_| {
698 *result = for_each_column_sequential(
699 region_bytes,
700 column_bytes,
701 source,
702 parity,
703 process,
704 );
705 });
706 }
707 });
708
709 return results.into_iter().collect();
710 }
711
712 for_each_column_sequential(shard_bytes, column_bytes, source, parity, process)
713}
714
715fn for_each_column_sequential<const NUM_SHARDS: usize, Source, Parity, Process>(
716 shard_bytes: usize,
717 column_bytes: usize,
718 source: [Source; NUM_SHARDS],
719 parity: [Parity; NUM_SHARDS],
720 process: Process,
721) -> Result<(), Error>
722where
723 Source: IntoColumns,
724 Parity: IntoColumns,
725 Process: Fn(usize, [Source; NUM_SHARDS], [Parity; NUM_SHARDS]) -> Result<(), Error>,
726{
727 let columns =
728 columns(shard_bytes, column_bytes, source).zip(columns(shard_bytes, column_bytes, parity));
729
730 for ((shard_bytes, source), (_, parity)) in columns {
731 process(shard_bytes, source, parity)?;
732 }
733
734 Ok(())
735}
736
737fn encode<const NUM_SHARDS: usize, Source, Parity>(
739 shard_bytes: usize,
740 source: &[Source; NUM_SHARDS],
741 parity: &mut [Parity; NUM_SHARDS],
742) -> Result<(), Error>
743where
744 Source: AsRef<[u8]>,
745 Parity: AsMut<[u8]>,
746{
747 let mut encoder = new_encoder::<NUM_SHARDS>(shard_bytes)?;
748
749 for shard in source {
750 encoder.add_original_shard(shard)?;
751 }
752
753 let result = encoder.encode()?;
754
755 for (input, output) in result.recovery_iter().zip(parity) {
756 output.as_mut().copy_from_slice(input);
757 }
758
759 Ok(())
760}
761
762fn decode_source<'a, const NUM_SHARDS: usize, Source, Parity>(
764 shard_bytes: usize,
765 source: &mut [Source; NUM_SHARDS],
766 source_present: &ShardsBitmap<NUM_SHARDS>,
767 parity: Parity,
768) -> Result<(), Error>
769where
770 Source: AsRef<[u8]> + AsMut<[u8]>,
771 Parity: IntoIterator<Item = Option<&'a [u8]>>,
772{
773 let mut decoder = new_decoder::<NUM_SHARDS>(shard_bytes)?;
774
775 for (index, shard) in source.iter().enumerate() {
776 if source_present.get(index) {
777 decoder.add_original_shard(index, shard)?;
778 }
779 }
780 for (index, maybe_shard) in parity.into_iter().enumerate() {
781 if let Some(shard) = maybe_shard {
782 decoder.add_recovery_shard(index, shard)?;
783 }
784 }
785
786 let result = decoder.decode()?;
787
788 for (index, shard) in source.iter_mut().enumerate() {
789 if !source_present.get(index) {
790 shard
791 .as_mut()
792 .copy_from_slice(restored_original(&result, index));
793 }
794 }
795
796 Ok(())
797}
798
799fn decode_all<const NUM_SHARDS: usize, Source, Parity>(
801 shard_bytes: usize,
802 source: &mut [Source; NUM_SHARDS],
803 parity: &mut [Parity; NUM_SHARDS],
804 present: &ShardsPresent<NUM_SHARDS>,
805) -> Result<(), Error>
806where
807 Source: AsRef<[u8]> + AsMut<[u8]>,
808 Parity: AsRef<[u8]> + AsMut<[u8]>,
809{
810 decode_source(
811 shard_bytes,
812 source,
813 &present.source,
814 parity
815 .iter()
816 .enumerate()
817 .map(|(index, shard)| present.parity.get(index).then_some(shard.as_ref())),
818 )?;
819
820 if present.parity.count() == NUM_SHARDS {
821 return Ok(());
822 }
823
824 let mut encoder = new_encoder::<NUM_SHARDS>(shard_bytes)?;
826
827 for shard in source.iter() {
828 encoder.add_original_shard(shard)?;
829 }
830
831 let result = encoder.encode()?;
832
833 for (index, shard) in parity.iter_mut().enumerate() {
834 if !present.parity.get(index) {
835 shard.as_mut().copy_from_slice(recovery(&result, index));
836 }
837 }
838
839 Ok(())
840}
841
842#[inline(always)]
845fn new_encoder<const NUM_SHARDS: usize>(
846 shard_bytes: usize,
847) -> Result<HighRateEncoder<DefaultEngine>, Error> {
848 HighRateEncoder::new(
849 NUM_SHARDS,
850 NUM_SHARDS,
851 shard_bytes,
852 DefaultEngine::new(),
853 None,
854 )
855}
856
857#[inline(always)]
858fn new_decoder<const NUM_SHARDS: usize>(
859 shard_bytes: usize,
860) -> Result<HighRateDecoder<DefaultEngine>, Error> {
861 HighRateDecoder::new(
862 NUM_SHARDS,
863 NUM_SHARDS,
864 shard_bytes,
865 DefaultEngine::new(),
866 None,
867 )
868}
869
870#[inline(always)]
871fn restored_original<'a>(
872 result: &'a reed_solomon_simd::DecoderResult<'_>,
873 index: usize,
874) -> &'a [u8] {
875 result
876 .restored_original(index)
877 .expect("Always corresponds to a missing source shard; qed")
878}
879
880#[inline(always)]
881fn recovery<'a>(result: &'a reed_solomon_simd::EncoderResult<'_>, index: usize) -> &'a [u8] {
882 result
883 .recovery(index)
884 .expect("Always corresponds to a missing parity shard; qed")
885}