Skip to main content

rustfs_erasure_codec/core/
reconstruct.rs

1extern crate alloc;
2
3use alloc::sync::Arc;
4use alloc::vec;
5use alloc::vec::Vec;
6
7use smallvec::SmallVec;
8
9use crate::errors::Error;
10use crate::{Field, ReconstructShard};
11
12#[cfg(feature = "std")]
13use rayon::prelude::*;
14#[cfg(feature = "std")]
15use std::sync::atomic::Ordering;
16
17#[cfg(not(feature = "std"))]
18use super::ReedSolomon;
19#[cfg(feature = "std")]
20use super::{ParallelPolicy, ReedSolomon};
21
22impl<F: Field> ReedSolomon<F> {
23    #[cfg(feature = "std")]
24    pub(crate) fn code_some_slices_par_raw(
25        &self,
26        matrix_rows: &[&[F::Elem]],
27        inputs: &[&[F::Elem]],
28        outputs: &mut [&mut [F::Elem]],
29    ) where
30        F::Elem: Send + Sync,
31    {
32        let shard_len = inputs.first().map(|input| input.len()).unwrap_or(0);
33        if shard_len == 0 {
34            return;
35        }
36
37        if outputs.len() <= 2 {
38            self.code_some_slices_one_or_two_outputs_reconstruct_data_par_raw(
39                matrix_rows,
40                inputs,
41                outputs,
42            );
43            return;
44        }
45
46        let decision = self.parallel_policy(shard_len, outputs.len());
47        if !decision.use_parallel {
48            self.code_some_slices_chunked(matrix_rows, inputs, outputs);
49            return;
50        }
51        self.code_some_slices_par_chunked(matrix_rows, inputs, outputs, decision.chunk_len);
52    }
53
54    #[cfg(feature = "std")]
55    pub(crate) fn code_some_slices_with_policy_raw(
56        &self,
57        matrix_rows: &[&[F::Elem]],
58        inputs: &[&[F::Elem]],
59        outputs: &mut [&mut [F::Elem]],
60        policy: ParallelPolicy,
61    ) where
62        F::Elem: Send + Sync,
63    {
64        let shard_len = inputs.first().map(|input| input.len()).unwrap_or(0);
65        if shard_len == 0 {
66            return;
67        }
68
69        if outputs.len() <= 2 {
70            self.code_some_slices_one_or_two_outputs_reconstruct_data_par_raw(
71                matrix_rows,
72                inputs,
73                outputs,
74            );
75            return;
76        }
77
78        let decision = policy.decide(
79            shard_len,
80            self.data_shard_count,
81            outputs.len(),
82            self.policy_cache.available_parallelism,
83        );
84        self.runtime_profile_metrics
85            .record_parallel_policy(decision);
86        if !decision.use_parallel {
87            self.code_some_slices_chunked(matrix_rows, inputs, outputs);
88            return;
89        }
90        self.code_some_slices_par_chunked(matrix_rows, inputs, outputs, decision.chunk_len);
91    }
92
93    #[cfg(feature = "std")]
94    pub(crate) fn code_some_slices_two_outputs_reconstruct_data_par_raw(
95        &self,
96        matrix_rows: &[&[F::Elem]],
97        inputs: &[&[F::Elem]],
98        outputs: &mut [&mut [F::Elem]],
99    ) where
100        F::Elem: Send + Sync,
101    {
102        debug_assert_eq!(2, matrix_rows.len());
103        debug_assert_eq!(2, outputs.len());
104
105        let shard_len = inputs.first().map(|input| input.len()).unwrap_or(0);
106        if shard_len == 0 {
107            return;
108        }
109
110        let decision = self.parallel_policy(shard_len, outputs.len());
111        self.runtime_profile_metrics
112            .record_parallel_policy(decision);
113        if !decision.use_parallel {
114            self.code_some_slices_chunked(matrix_rows, inputs, outputs);
115            return;
116        }
117
118        let chunk_len = if self.data_shard_count <= 16 {
119            core::cmp::min(shard_len, core::cmp::max(decision.chunk_len, 512 * 1024))
120        } else {
121            decision.chunk_len
122        };
123        self.runtime_profile_metrics.record_code_some(
124            true,
125            shard_len,
126            inputs.len(),
127            outputs.len(),
128            chunk_len,
129        );
130
131        let data_shard_count = self.data_shard_count;
132        outputs
133            .par_iter_mut()
134            .enumerate()
135            .for_each(|(i_row, output)| {
136                let matrix_row = matrix_rows[i_row];
137
138                let mut start = 0;
139                while start < shard_len {
140                    let end = core::cmp::min(start + chunk_len, shard_len);
141                    let output_chunk = &mut output[start..end];
142
143                    F::mul_slice(matrix_row[0], &inputs[0][start..end], output_chunk);
144                    for i_input in 1..data_shard_count {
145                        F::mul_slice_add(
146                            matrix_row[i_input],
147                            &inputs[i_input][start..end],
148                            output_chunk,
149                        );
150                    }
151
152                    start = end;
153                }
154            });
155    }
156
157    #[cfg(feature = "std")]
158    pub(crate) fn code_some_slices_one_or_two_outputs_reconstruct_data_par_raw(
159        &self,
160        matrix_rows: &[&[F::Elem]],
161        inputs: &[&[F::Elem]],
162        outputs: &mut [&mut [F::Elem]],
163    ) where
164        F::Elem: Send + Sync,
165    {
166        debug_assert!((1..=2).contains(&outputs.len()));
167
168        if outputs.len() == 1 {
169            let shard_len = inputs.first().map(|input| input.len()).unwrap_or(0);
170            if shard_len == 0 {
171                return;
172            }
173
174            let decision = self.parallel_policy(shard_len, outputs.len());
175            self.runtime_profile_metrics
176                .record_parallel_policy(decision);
177            if !decision.use_parallel {
178                self.code_some_slices_chunked(matrix_rows, inputs, outputs);
179                return;
180            }
181
182            self.runtime_profile_metrics.record_code_some(
183                true,
184                shard_len,
185                inputs.len(),
186                outputs.len(),
187                decision.chunk_len,
188            );
189
190            let data_shard_count = self.data_shard_count;
191            let matrix_row = matrix_rows[0];
192            outputs[0]
193                .par_chunks_mut(decision.chunk_len)
194                .enumerate()
195                .for_each(|(chunk_idx, output_chunk)| {
196                    let start = chunk_idx * decision.chunk_len;
197                    let end = start + output_chunk.len();
198
199                    F::mul_slice(matrix_row[0], &inputs[0][start..end], output_chunk);
200                    for i_input in 1..data_shard_count {
201                        F::mul_slice_add(
202                            matrix_row[i_input],
203                            &inputs[i_input][start..end],
204                            output_chunk,
205                        );
206                    }
207                });
208            return;
209        }
210
211        self.code_some_slices_two_outputs_reconstruct_data_par_raw(matrix_rows, inputs, outputs);
212    }
213
214    #[cfg(feature = "std")]
215    pub(crate) fn reconstruct_internal_option_vec_par(
216        &self,
217        shards: &mut [Option<Vec<F::Elem>>],
218        data_only: bool,
219    ) -> Result<(), Error>
220    where
221        F::Elem: Send + Sync,
222    {
223        let (data_policy, parity_policy) = self.policy_cache.reconstruct_stage_policies(data_only);
224        self.reconstruct_internal_option_vec_par_with_stage_policies(
225            shards,
226            data_only,
227            data_policy,
228            parity_policy,
229        )
230    }
231
232    #[cfg(feature = "std")]
233    pub(crate) fn reconstruct_internal_option_vec_par_with_stage_policies(
234        &self,
235        shards: &mut [Option<Vec<F::Elem>>],
236        data_only: bool,
237        data_policy: ParallelPolicy,
238        parity_policy: ParallelPolicy,
239    ) -> Result<(), Error>
240    where
241        F::Elem: Send + Sync,
242    {
243        check_piece_count!(all => self, shards);
244
245        let data_shard_count = self.data_shard_count;
246
247        let mut number_present = 0;
248        let mut shard_len = None;
249        for shard in shards.iter() {
250            if let Some(shard) = shard.as_ref() {
251                let len = shard.len();
252                if len == 0 {
253                    return Err(Error::EmptyShard);
254                }
255                number_present += 1;
256                if let Some(old_len) = shard_len
257                    && len != old_len
258                {
259                    return Err(Error::IncorrectShardSize);
260                }
261                shard_len = Some(len);
262            }
263        }
264
265        if number_present == self.total_shard_count {
266            self.runtime_profile_metrics
267                .record_reconstruct(data_only, 0, 0, true);
268            return Ok(());
269        }
270        if number_present < data_shard_count {
271            return Err(Error::TooFewShardsPresent);
272        }
273
274        let shard_len = shard_len.expect("at least one shard present; qed");
275
276        let mut valid_indices: SmallVec<[usize; 32]> = SmallVec::with_capacity(data_shard_count);
277        let mut invalid_indices: SmallVec<[usize; 32]> =
278            SmallVec::with_capacity(self.total_shard_count);
279        let mut missing_data_indices: SmallVec<[usize; 32]> = SmallVec::new();
280        let mut missing_parity_indices: SmallVec<[usize; 32]> = SmallVec::new();
281
282        for (matrix_row, shard) in shards.iter().enumerate() {
283            match shard.as_ref() {
284                Some(_shard) => {
285                    if valid_indices.len() < data_shard_count {
286                        valid_indices.push(matrix_row);
287                    }
288                }
289                None => {
290                    invalid_indices.push(matrix_row);
291                    if matrix_row < data_shard_count {
292                        missing_data_indices.push(matrix_row);
293                    } else if !data_only {
294                        missing_parity_indices.push(matrix_row);
295                    }
296                }
297            }
298        }
299
300        self.runtime_profile_metrics.record_reconstruct(
301            data_only,
302            missing_data_indices.len(),
303            missing_parity_indices.len(),
304            false,
305        );
306
307        let data_decode_matrix = self.get_data_decode_matrix(&valid_indices, &invalid_indices)?;
308
309        if !missing_data_indices.is_empty() {
310            #[cfg(feature = "std")]
311            self.runtime_profile_metrics
312                .record_reconstruct_data_stage(shard_len, missing_data_indices.len());
313            let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
314                SmallVec::with_capacity(missing_data_indices.len());
315            for &idx in &missing_data_indices {
316                matrix_rows.push(data_decode_matrix.get_row(idx));
317            }
318
319            let mut recovered_data: Vec<Vec<F::Elem>> = missing_data_indices
320                .iter()
321                .map(|_| vec![F::zero(); shard_len])
322                .collect();
323
324            {
325                let mut sub_shards: SmallVec<[&[F::Elem]; 32]> =
326                    SmallVec::with_capacity(data_shard_count);
327                for &idx in &valid_indices {
328                    let shard = shards[idx].as_deref().ok_or(Error::TooFewShardsPresent)?;
329                    sub_shards.push(shard);
330                }
331
332                let mut outputs: SmallVec<[&mut [F::Elem]; 32]> = recovered_data
333                    .iter_mut()
334                    .map(|shard| shard.as_mut_slice())
335                    .collect();
336
337                if data_only && outputs.len() <= 2 {
338                    self.runtime_profile_metrics
339                        .record_reconstruct_data_small_output_specialized();
340                    self.code_some_slices_one_or_two_outputs_reconstruct_data_par_raw(
341                        &matrix_rows,
342                        &sub_shards,
343                        &mut outputs,
344                    );
345                } else {
346                    self.code_some_slices_with_policy_raw(
347                        &matrix_rows,
348                        &sub_shards,
349                        &mut outputs,
350                        data_policy,
351                    );
352                }
353            }
354
355            for (idx, recovered) in missing_data_indices.into_iter().zip(recovered_data) {
356                shards[idx] = Some(recovered);
357            }
358        }
359
360        if data_only {
361            return Ok(());
362        }
363
364        if missing_parity_indices.is_empty() {
365            return Ok(());
366        }
367
368        #[cfg(feature = "std")]
369        self.runtime_profile_metrics
370            .record_reconstruct_parity_stage(shard_len, missing_parity_indices.len());
371        let parity_rows = self.get_parity_rows();
372        let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
373            SmallVec::with_capacity(missing_parity_indices.len());
374        for &idx in &missing_parity_indices {
375            matrix_rows.push(parity_rows[idx - data_shard_count]);
376        }
377
378        let mut recovered_parity: Vec<Vec<F::Elem>> = missing_parity_indices
379            .iter()
380            .map(|_| vec![F::zero(); shard_len])
381            .collect();
382
383        {
384            let mut all_data: SmallVec<[&[F::Elem]; 32]> =
385                SmallVec::with_capacity(data_shard_count);
386            for shard in shards.iter().take(data_shard_count) {
387                let shard = shard.as_deref().ok_or(Error::TooFewShardsPresent)?;
388                all_data.push(shard);
389            }
390
391            let mut outputs: SmallVec<[&mut [F::Elem]; 32]> = recovered_parity
392                .iter_mut()
393                .map(|shard| shard.as_mut_slice())
394                .collect();
395
396            self.code_some_slices_with_policy_raw(
397                &matrix_rows,
398                &all_data,
399                &mut outputs,
400                parity_policy,
401            );
402        }
403        for (idx, recovered) in missing_parity_indices.into_iter().zip(recovered_parity) {
404            shards[idx] = Some(recovered);
405        }
406        Ok(())
407    }
408
409    #[cfg(feature = "std")]
410    pub(crate) fn reconstruct_internal_option_vec_par_with_policy(
411        &self,
412        shards: &mut [Option<Vec<F::Elem>>],
413        data_only: bool,
414        policy: ParallelPolicy,
415    ) -> Result<(), Error>
416    where
417        F::Elem: Send + Sync,
418    {
419        self.reconstruct_internal_option_vec_par_with_stage_policies(
420            shards, data_only, policy, policy,
421        )
422    }
423
424    /// Reconstruct all missing shards (data and parity).
425    ///
426    /// Missing shards should be zero-length; present shards must have valid data.
427    pub fn reconstruct<T: ReconstructShard<F>>(&self, slices: &mut [T]) -> Result<(), Error> {
428        if self.is_leopard_gf8_family() {
429            return self.reconstruct_leopard_gf8(slices, false);
430        }
431        if self.is_leopard_gf16_family() {
432            return self.reconstruct_leopard_gf16(slices, false);
433        }
434        self.ensure_classic_family_execution()?;
435        self.reconstruct_internal(slices, false)
436    }
437
438    /// Reconstruct only missing data shards (parity shards are not recovered).
439    pub fn reconstruct_data<T: ReconstructShard<F>>(&self, slices: &mut [T]) -> Result<(), Error> {
440        if self.is_leopard_gf8_family() {
441            return self.reconstruct_leopard_gf8(slices, true);
442        }
443        if self.is_leopard_gf16_family() {
444            return self.reconstruct_leopard_gf16(slices, true);
445        }
446        self.ensure_classic_family_execution()?;
447        self.reconstruct_internal(slices, true)
448    }
449
450    /// Reconstruct only shards marked `true` in the `required` mask.
451    pub fn reconstruct_some<T: ReconstructShard<F>>(
452        &self,
453        shards: &mut [T],
454        required: &[bool],
455    ) -> Result<(), Error> {
456        if self.is_leopard_gf8_family() {
457            // For leopard, reconstruct_some delegates to reconstruct (leopard always
458            // recovers all shards, then the caller picks which ones they needed).
459            self.reconstruct_leopard_gf8(shards, false)?;
460            return Ok(());
461        }
462        if self.is_leopard_gf16_family() {
463            self.reconstruct_leopard_gf16(shards, false)?;
464            return Ok(());
465        }
466        self.ensure_classic_family_execution()?;
467        if required.len() != self.total_shard_count {
468            return Err(Error::InvalidShardFlags);
469        }
470
471        check_piece_count!(all => self, shards);
472
473        let mut number_present = 0;
474        let mut shard_len = None;
475
476        for shard in shards.iter_mut() {
477            if let Some(len) = shard.len() {
478                if len == 0 {
479                    return Err(Error::EmptyShard);
480                }
481                number_present += 1;
482                if let Some(old_len) = shard_len
483                    && len != old_len
484                {
485                    return Err(Error::IncorrectShardSize);
486                }
487                shard_len = Some(len);
488            }
489        }
490
491        if number_present == self.total_shard_count {
492            return Ok(());
493        }
494
495        if number_present < self.data_shard_count {
496            return Err(Error::TooFewShardsPresent);
497        }
498
499        let shard_len = shard_len.expect("at least one shard present; qed");
500        let required_data_only = required
501            .iter()
502            .enumerate()
503            .all(|(i, required)| !*required || i < self.data_shard_count);
504
505        let originally_missing: Vec<bool> = shards
506            .iter_mut()
507            .map(|shard| shard.get().is_none())
508            .collect();
509
510        if required_data_only {
511            let mut valid_indices: SmallVec<[usize; 32]> =
512                SmallVec::with_capacity(self.data_shard_count);
513            let mut invalid_indices: SmallVec<[usize; 32]> =
514                SmallVec::with_capacity(self.total_shard_count);
515            let mut required_missing_data_indices: SmallVec<[usize; 32]> = SmallVec::new();
516
517            for (index, is_missing) in originally_missing.iter().copied().enumerate() {
518                if is_missing {
519                    invalid_indices.push(index);
520                    if index < self.data_shard_count && required[index] {
521                        required_missing_data_indices.push(index);
522                    }
523                } else if valid_indices.len() < self.data_shard_count {
524                    valid_indices.push(index);
525                }
526            }
527
528            if required_missing_data_indices.is_empty() {
529                return Ok(());
530            }
531
532            let data_decode_matrix =
533                self.get_data_decode_matrix(&valid_indices, &invalid_indices)?;
534            let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
535                SmallVec::with_capacity(required_missing_data_indices.len());
536            for &idx in &required_missing_data_indices {
537                matrix_rows.push(data_decode_matrix.get_row(idx));
538            }
539
540            // Borrow present input shards directly and defer output mutation until
541            // after compute completes, so the required-data-only path avoids
542            // cloning every valid shard into temporary owned buffers.
543            let mut sub_shard_ptrs: SmallVec<[(*const F::Elem, usize); 32]> =
544                SmallVec::with_capacity(valid_indices.len());
545            for &idx in &valid_indices {
546                let shard = shards[idx]
547                    .get()
548                    .expect("valid shard index must be present");
549                sub_shard_ptrs.push((shard.as_ptr(), shard.len()));
550            }
551
552            let sub_shards: SmallVec<[&[F::Elem]; 32]> = sub_shard_ptrs
553                .iter()
554                .map(|&(ptr, len)| {
555                    // SAFETY: each pointer/length pair was captured from a present
556                    // shard in `shards` after full length validation. The compute
557                    // phase only reads from these inputs and writes into separate
558                    // owned recovery buffers, so the pointed-to shard storage
559                    // remains valid and unmodified for the duration of these
560                    // borrowed slices.
561                    unsafe { core::slice::from_raw_parts(ptr, len) }
562                })
563                .collect();
564            let mut output_ptrs: SmallVec<[(*mut F::Elem, usize); 32]> =
565                SmallVec::with_capacity(required_missing_data_indices.len());
566            for &idx in &required_missing_data_indices {
567                match shards[idx].get_or_initialize(shard_len) {
568                    Ok(dst) | Err(Ok(dst)) => output_ptrs.push((dst.as_mut_ptr(), dst.len())),
569                    Err(Err(err)) => return Err(err),
570                }
571            }
572            let mut outputs: SmallVec<[&mut [F::Elem]; 32]> = output_ptrs
573                .iter()
574                .map(|&(ptr, len)| {
575                    // SAFETY: each pointer/length pair came from a distinct
576                    // `get_or_initialize(shard_len)` call above after all
577                    // validation completed. Required missing indices are unique,
578                    // and this compute phase mutates only those output buffers.
579                    unsafe { core::slice::from_raw_parts_mut(ptr, len) }
580                })
581                .collect();
582            self.code_some_slices(&matrix_rows, &sub_shards, &mut outputs);
583            drop(outputs);
584        } else {
585            let mut working: Vec<Option<Vec<F::Elem>>> = shards
586                .iter_mut()
587                .map(|shard| shard.get().map(|data| data.to_vec()))
588                .collect();
589            self.reconstruct(&mut working)?;
590
591            for (i, shard) in shards.iter_mut().enumerate() {
592                if !required[i] || !originally_missing[i] {
593                    continue;
594                }
595
596                let recovered = working[i]
597                    .as_ref()
598                    .expect("recovered shard must be present");
599                match shard.get_or_initialize(shard_len) {
600                    Ok(dst) | Err(Ok(dst)) => dst.copy_from_slice(recovered),
601                    Err(Err(err)) => return Err(err),
602                }
603            }
604        }
605
606        Ok(())
607    }
608
609    /// Shared implementation for Leopard GF8/GF16 reconstruction.
610    ///
611    /// Builds the `present`, `outputs`, and `input_data` arrays required by
612    /// the Forney-based FFT decoder, calls the provided `reconstruct_fn`,
613    /// then writes recovered data back into the original shard objects.
614    ///
615    /// Only copies present shard data into output buffers for missing shards
616    /// (the Leopard decoder skips present positions). This avoids the overhead
617    /// of copying all present shard data into temporary buffers.
618    #[allow(clippy::needless_range_loop, clippy::type_complexity)]
619    fn reconstruct_leopard_impl<T: ReconstructShard<F>>(
620        &self,
621        slices: &mut [T],
622        reconstruct_fn: fn(
623            &[bool],
624            &mut [&mut [u8]],
625            &[Option<&[u8]>],
626            usize,
627            usize,
628        ) -> Result<(), Error>,
629    ) -> Result<(), Error> {
630        check_piece_count!(all => self, slices);
631
632        let total = self.total_shard_count;
633        let shard_len_opt: Option<usize> = slices.iter().find_map(|s| s.len());
634        let Some(shard_len) = shard_len_opt else {
635            return Err(Error::EmptyShard);
636        };
637
638        // SAFETY: F::Elem = u8 for all leopard codec families.
639        let mut present = vec![false; total];
640        let mut raw_data: Vec<Option<*const u8>> = vec![None; total];
641        for i in 0..total {
642            if let Some(data) = slices[i].get() {
643                present[i] = true;
644                raw_data[i] = Some(data as *const [F::Elem] as *const u8);
645            }
646        }
647
648        // Allocate output buffers. Present shards get zeroed buffers (the decoder
649        // skips them via `if present[i] { continue; }`). Missing shards also get
650        // zeroed buffers which the decoder writes recovered data into.
651        let mut output_bufs: Vec<Vec<u8>> = (0..total).map(|_| vec![0u8; shard_len]).collect();
652
653        let mut outputs: Vec<&mut [u8]> = output_bufs
654            .iter_mut()
655            .map(|buf| buf.as_mut_slice())
656            .collect();
657
658        let mut input_data: Vec<Option<&[u8]>> = Vec::with_capacity(total);
659        for i in 0..total {
660            if let Some(ptr) = raw_data[i] {
661                let src: &[u8] = unsafe { core::slice::from_raw_parts(ptr, shard_len) };
662                input_data.push(Some(src));
663            } else {
664                input_data.push(None);
665            }
666        }
667
668        reconstruct_fn(
669            &present,
670            &mut outputs,
671            &input_data,
672            self.data_shard_count,
673            self.parity_shard_count,
674        )?;
675
676        for i in 0..total {
677            if present[i] {
678                continue;
679            }
680            let elem_slice: &[F::Elem] =
681                unsafe { &*(output_bufs[i].as_slice() as *const [u8] as *const [F::Elem]) };
682            match slices[i].get_or_initialize(shard_len) {
683                Ok(dst) | Err(Ok(dst)) => dst.copy_from_slice(elem_slice),
684                Err(Err(err)) => return Err(err),
685            }
686        }
687
688        Ok(())
689    }
690
691    fn reconstruct_leopard_gf8<T: ReconstructShard<F>>(
692        &self,
693        slices: &mut [T],
694        _data_only: bool,
695    ) -> Result<(), Error> {
696        self.reconstruct_leopard_impl(slices, super::leopard_gf8::reconstruct_with_tables)
697    }
698
699    fn reconstruct_leopard_gf16<T: ReconstructShard<F>>(
700        &self,
701        slices: &mut [T],
702        _data_only: bool,
703    ) -> Result<(), Error> {
704        self.reconstruct_leopard_impl(slices, super::leopard::leopard_gf16_reconstruct)
705    }
706
707    pub(crate) fn get_data_decode_matrix(
708        &self,
709        valid_indices: &[usize],
710        invalid_indices: &[usize],
711    ) -> Result<Arc<crate::matrix::Matrix<F>>, Error> {
712        if valid_indices.len() != self.data_shard_count {
713            return Err(Error::TooFewShardsPresent);
714        }
715
716        if self.options.inversion_cache {
717            #[cfg(feature = "std")]
718            self.reconstruction_cache_metrics
719                .requests
720                .fetch_add(1, Ordering::Relaxed);
721
722            let mut cache = self.data_decode_matrix_cache.lock();
723            if let Some(entry) = cache.get(invalid_indices) {
724                #[cfg(feature = "std")]
725                self.reconstruction_cache_metrics
726                    .hits
727                    .fetch_add(1, Ordering::Relaxed);
728                return Ok(entry.clone());
729            }
730
731            #[cfg(feature = "std")]
732            self.reconstruction_cache_metrics
733                .misses
734                .fetch_add(1, Ordering::Relaxed);
735        }
736        let mut sub_matrix =
737            crate::matrix::Matrix::new(self.data_shard_count, self.data_shard_count);
738        for (sub_matrix_row, &valid_index) in valid_indices.iter().enumerate() {
739            for c in 0..self.data_shard_count {
740                sub_matrix.set(sub_matrix_row, c, self.matrix.get(valid_index, c));
741            }
742        }
743        let data_decode_matrix = Arc::new(
744            sub_matrix
745                .invert()
746                .map_err(|_| Error::InvalidCustomMatrix)?,
747        );
748        if self.options.inversion_cache {
749            let data_decode_matrix = data_decode_matrix.clone();
750            let mut cache = self.data_decode_matrix_cache.lock();
751            #[cfg(feature = "std")]
752            let before_len = cache.len();
753            #[cfg(feature = "std")]
754            let capacity = cache.capacity();
755            cache.insert(Vec::from(invalid_indices), data_decode_matrix);
756            #[cfg(feature = "std")]
757            if capacity > 0 && before_len >= capacity {
758                self.reconstruction_cache_metrics
759                    .evictions
760                    .fetch_add(1, Ordering::Relaxed);
761            }
762            #[cfg(feature = "std")]
763            self.reconstruction_cache_metrics
764                .inserts
765                .fetch_add(1, Ordering::Relaxed);
766        }
767        Ok(data_decode_matrix)
768    }
769
770    fn reconstruct_internal<T: ReconstructShard<F>>(
771        &self,
772        shards: &mut [T],
773        data_only: bool,
774    ) -> Result<(), Error> {
775        check_piece_count!(all => self, shards);
776
777        let data_shard_count = self.data_shard_count;
778
779        let mut number_present = 0;
780        let mut shard_len = None;
781
782        for shard in shards.iter_mut() {
783            if let Some(len) = shard.len() {
784                if len == 0 {
785                    return Err(Error::EmptyShard);
786                }
787                number_present += 1;
788                if let Some(old_len) = shard_len
789                    && len != old_len
790                {
791                    return Err(Error::IncorrectShardSize);
792                }
793                shard_len = Some(len);
794            }
795        }
796
797        if number_present == self.total_shard_count {
798            #[cfg(feature = "std")]
799            self.runtime_profile_metrics
800                .record_reconstruct(data_only, 0, 0, true);
801            return Ok(());
802        }
803
804        if number_present < data_shard_count {
805            return Err(Error::TooFewShardsPresent);
806        }
807
808        let shard_len = shard_len.expect("at least one shard present; qed");
809
810        let mut sub_shards: SmallVec<[&[F::Elem]; 32]> = SmallVec::with_capacity(data_shard_count);
811        let mut missing_data_slices: SmallVec<[&mut [F::Elem]; 32]> =
812            SmallVec::with_capacity(self.parity_shard_count);
813        let mut missing_parity_slices: SmallVec<[&mut [F::Elem]; 32]> =
814            SmallVec::with_capacity(self.parity_shard_count);
815        let mut valid_indices: SmallVec<[usize; 32]> = SmallVec::with_capacity(data_shard_count);
816        let mut invalid_indices: SmallVec<[usize; 32]> = SmallVec::with_capacity(data_shard_count);
817
818        for (matrix_row, shard) in shards.iter_mut().enumerate() {
819            let shard_data = if matrix_row >= data_shard_count && data_only {
820                shard.get().ok_or(None)
821            } else {
822                shard.get_or_initialize(shard_len).map_err(Some)
823            };
824
825            match shard_data {
826                Ok(shard) => {
827                    if sub_shards.len() < data_shard_count {
828                        sub_shards.push(shard);
829                        valid_indices.push(matrix_row);
830                    }
831                }
832                Err(None) => {
833                    invalid_indices.push(matrix_row);
834                }
835                Err(Some(x)) => {
836                    let shard = x?;
837                    if matrix_row < data_shard_count {
838                        missing_data_slices.push(shard);
839                    } else {
840                        missing_parity_slices.push(shard);
841                    }
842
843                    invalid_indices.push(matrix_row);
844                }
845            }
846        }
847
848        #[cfg(feature = "std")]
849        {
850            let missing_data_count = invalid_indices
851                .iter()
852                .filter(|&&i| i < data_shard_count)
853                .count();
854            let missing_parity_count = if data_only {
855                0
856            } else {
857                invalid_indices
858                    .iter()
859                    .filter(|&&i| i >= data_shard_count)
860                    .count()
861            };
862            self.runtime_profile_metrics.record_reconstruct(
863                data_only,
864                missing_data_count,
865                missing_parity_count,
866                false,
867            );
868        }
869
870        let data_decode_matrix = self.get_data_decode_matrix(&valid_indices, &invalid_indices)?;
871
872        let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
873            SmallVec::with_capacity(self.parity_shard_count);
874
875        for i_slice in invalid_indices
876            .iter()
877            .cloned()
878            .take_while(|i| i < &data_shard_count)
879        {
880            matrix_rows.push(data_decode_matrix.get_row(i_slice));
881        }
882
883        #[cfg(feature = "std")]
884        self.runtime_profile_metrics
885            .record_reconstruct_data_stage(shard_len, matrix_rows.len());
886        self.code_some_slices(&matrix_rows, &sub_shards, &mut missing_data_slices);
887
888        if data_only {
889            Ok(())
890        } else {
891            let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
892                SmallVec::with_capacity(self.parity_shard_count);
893            let parity_rows = self.get_parity_rows();
894
895            for i_slice in invalid_indices
896                .iter()
897                .cloned()
898                .skip_while(|i| i < &data_shard_count)
899            {
900                matrix_rows.push(parity_rows[i_slice - data_shard_count]);
901            }
902            #[cfg(feature = "std")]
903            self.runtime_profile_metrics
904                .record_reconstruct_parity_stage(shard_len, matrix_rows.len());
905            {
906                let mut i_old_data_slice = 0;
907                let mut i_new_data_slice = 0;
908
909                let mut all_data_slices: SmallVec<[&[F::Elem]; 32]> =
910                    SmallVec::with_capacity(data_shard_count);
911
912                let mut next_maybe_good = 0;
913                let mut push_good_up_to = move |data_slices: &mut SmallVec<_>, up_to| {
914                    for _ in next_maybe_good..up_to {
915                        data_slices.push(sub_shards[i_old_data_slice]);
916                        i_old_data_slice += 1;
917                    }
918
919                    next_maybe_good = up_to + 1;
920                };
921
922                for i_slice in invalid_indices
923                    .iter()
924                    .cloned()
925                    .take_while(|i| i < &data_shard_count)
926                {
927                    push_good_up_to(&mut all_data_slices, i_slice);
928                    all_data_slices.push(missing_data_slices[i_new_data_slice]);
929                    i_new_data_slice += 1;
930                }
931                push_good_up_to(&mut all_data_slices, data_shard_count);
932
933                self.code_some_slices(&matrix_rows, &all_data_slices, &mut missing_parity_slices);
934            }
935
936            Ok(())
937        }
938    }
939}