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        // invariant: reached only when number_present >= data_shard_count >= 1
275        // (see the TooFewDataShards guard in new()), so at least one present
276        // shard has set shard_len to Some.
277        let shard_len = shard_len.expect("at least one shard present; qed");
278
279        let mut valid_indices: SmallVec<[usize; 32]> = SmallVec::with_capacity(data_shard_count);
280        let mut invalid_indices: SmallVec<[usize; 32]> =
281            SmallVec::with_capacity(self.total_shard_count);
282        let mut missing_data_indices: SmallVec<[usize; 32]> = SmallVec::new();
283        let mut missing_parity_indices: SmallVec<[usize; 32]> = SmallVec::new();
284
285        for (matrix_row, shard) in shards.iter().enumerate() {
286            match shard.as_ref() {
287                Some(_shard) => {
288                    if valid_indices.len() < data_shard_count {
289                        valid_indices.push(matrix_row);
290                    }
291                }
292                None => {
293                    invalid_indices.push(matrix_row);
294                    if matrix_row < data_shard_count {
295                        missing_data_indices.push(matrix_row);
296                    } else if !data_only {
297                        missing_parity_indices.push(matrix_row);
298                    }
299                }
300            }
301        }
302
303        self.runtime_profile_metrics.record_reconstruct(
304            data_only,
305            missing_data_indices.len(),
306            missing_parity_indices.len(),
307            false,
308        );
309
310        let data_decode_matrix = self.get_data_decode_matrix(&valid_indices, &invalid_indices)?;
311
312        if !missing_data_indices.is_empty() {
313            #[cfg(feature = "std")]
314            self.runtime_profile_metrics
315                .record_reconstruct_data_stage(shard_len, missing_data_indices.len());
316            let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
317                SmallVec::with_capacity(missing_data_indices.len());
318            for &idx in &missing_data_indices {
319                matrix_rows.push(data_decode_matrix.get_row(idx));
320            }
321
322            let mut recovered_data: Vec<Vec<F::Elem>> = missing_data_indices
323                .iter()
324                .map(|_| vec![F::zero(); shard_len])
325                .collect();
326
327            {
328                let mut sub_shards: SmallVec<[&[F::Elem]; 32]> =
329                    SmallVec::with_capacity(data_shard_count);
330                for &idx in &valid_indices {
331                    let shard = shards[idx].as_deref().ok_or(Error::TooFewShardsPresent)?;
332                    sub_shards.push(shard);
333                }
334
335                let mut outputs: SmallVec<[&mut [F::Elem]; 32]> = recovered_data
336                    .iter_mut()
337                    .map(|shard| shard.as_mut_slice())
338                    .collect();
339
340                if data_only && outputs.len() <= 2 {
341                    self.runtime_profile_metrics
342                        .record_reconstruct_data_small_output_specialized();
343                    self.code_some_slices_one_or_two_outputs_reconstruct_data_par_raw(
344                        &matrix_rows,
345                        &sub_shards,
346                        &mut outputs,
347                    );
348                } else {
349                    self.code_some_slices_with_policy_raw(
350                        &matrix_rows,
351                        &sub_shards,
352                        &mut outputs,
353                        data_policy,
354                    );
355                }
356            }
357
358            for (idx, recovered) in missing_data_indices.into_iter().zip(recovered_data) {
359                shards[idx] = Some(recovered);
360            }
361        }
362
363        if data_only {
364            return Ok(());
365        }
366
367        if missing_parity_indices.is_empty() {
368            return Ok(());
369        }
370
371        #[cfg(feature = "std")]
372        self.runtime_profile_metrics
373            .record_reconstruct_parity_stage(shard_len, missing_parity_indices.len());
374        let parity_rows = self.get_parity_rows();
375        let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
376            SmallVec::with_capacity(missing_parity_indices.len());
377        for &idx in &missing_parity_indices {
378            matrix_rows.push(parity_rows[idx - data_shard_count]);
379        }
380
381        let mut recovered_parity: Vec<Vec<F::Elem>> = missing_parity_indices
382            .iter()
383            .map(|_| vec![F::zero(); shard_len])
384            .collect();
385
386        {
387            let mut all_data: SmallVec<[&[F::Elem]; 32]> =
388                SmallVec::with_capacity(data_shard_count);
389            for shard in shards.iter().take(data_shard_count) {
390                let shard = shard.as_deref().ok_or(Error::TooFewShardsPresent)?;
391                all_data.push(shard);
392            }
393
394            let mut outputs: SmallVec<[&mut [F::Elem]; 32]> = recovered_parity
395                .iter_mut()
396                .map(|shard| shard.as_mut_slice())
397                .collect();
398
399            self.code_some_slices_with_policy_raw(
400                &matrix_rows,
401                &all_data,
402                &mut outputs,
403                parity_policy,
404            );
405        }
406        for (idx, recovered) in missing_parity_indices.into_iter().zip(recovered_parity) {
407            shards[idx] = Some(recovered);
408        }
409        Ok(())
410    }
411
412    #[cfg(feature = "std")]
413    pub(crate) fn reconstruct_internal_option_vec_par_with_policy(
414        &self,
415        shards: &mut [Option<Vec<F::Elem>>],
416        data_only: bool,
417        policy: ParallelPolicy,
418    ) -> Result<(), Error>
419    where
420        F::Elem: Send + Sync,
421    {
422        self.reconstruct_internal_option_vec_par_with_stage_policies(
423            shards, data_only, policy, policy,
424        )
425    }
426
427    /// Reconstruct all missing shards (data and parity).
428    ///
429    /// Missing shards should be zero-length; present shards must have valid data.
430    pub fn reconstruct<T: ReconstructShard<F>>(&self, slices: &mut [T]) -> Result<(), Error> {
431        if self.is_leopard_gf8_family() {
432            return self.reconstruct_leopard_gf8(slices, false);
433        }
434        if self.is_leopard_gf16_family() {
435            return self.reconstruct_leopard_gf16(slices, false);
436        }
437        self.ensure_classic_family_execution()?;
438        self.reconstruct_internal(slices, false)
439    }
440
441    /// Reconstruct only missing data shards (parity shards are not recovered).
442    pub fn reconstruct_data<T: ReconstructShard<F>>(&self, slices: &mut [T]) -> Result<(), Error> {
443        if self.is_leopard_gf8_family() {
444            return self.reconstruct_leopard_gf8(slices, true);
445        }
446        if self.is_leopard_gf16_family() {
447            return self.reconstruct_leopard_gf16(slices, true);
448        }
449        self.ensure_classic_family_execution()?;
450        self.reconstruct_internal(slices, true)
451    }
452
453    /// Reconstruct only shards marked `true` in the `required` mask.
454    pub fn reconstruct_some<T: ReconstructShard<F>>(
455        &self,
456        shards: &mut [T],
457        required: &[bool],
458    ) -> Result<(), Error> {
459        if self.is_leopard_gf8_family() {
460            // For leopard, reconstruct_some delegates to reconstruct (leopard always
461            // recovers all shards, then the caller picks which ones they needed).
462            self.reconstruct_leopard_gf8(shards, false)?;
463            return Ok(());
464        }
465        if self.is_leopard_gf16_family() {
466            self.reconstruct_leopard_gf16(shards, false)?;
467            return Ok(());
468        }
469        self.ensure_classic_family_execution()?;
470        if required.len() != self.total_shard_count {
471            return Err(Error::InvalidShardFlags);
472        }
473
474        check_piece_count!(all => self, shards);
475
476        let mut number_present = 0;
477        let mut shard_len = None;
478
479        for shard in shards.iter_mut() {
480            if let Some(len) = shard.len() {
481                if len == 0 {
482                    return Err(Error::EmptyShard);
483                }
484                number_present += 1;
485                if let Some(old_len) = shard_len
486                    && len != old_len
487                {
488                    return Err(Error::IncorrectShardSize);
489                }
490                shard_len = Some(len);
491            }
492        }
493
494        if number_present == self.total_shard_count {
495            return Ok(());
496        }
497
498        if number_present < self.data_shard_count {
499            return Err(Error::TooFewShardsPresent);
500        }
501
502        // invariant: reached only when number_present >= data_shard_count >= 1
503        // (see the TooFewDataShards guard in new()), so at least one present
504        // shard has set shard_len to Some.
505        let shard_len = shard_len.expect("at least one shard present; qed");
506        let required_data_only = required
507            .iter()
508            .enumerate()
509            .all(|(i, required)| !*required || i < self.data_shard_count);
510
511        let originally_missing: Vec<bool> = shards
512            .iter_mut()
513            .map(|shard| shard.get().is_none())
514            .collect();
515
516        if required_data_only {
517            let mut valid_indices: SmallVec<[usize; 32]> =
518                SmallVec::with_capacity(self.data_shard_count);
519            let mut invalid_indices: SmallVec<[usize; 32]> =
520                SmallVec::with_capacity(self.total_shard_count);
521            let mut required_missing_data_indices: SmallVec<[usize; 32]> = SmallVec::new();
522
523            for (index, is_missing) in originally_missing.iter().copied().enumerate() {
524                if is_missing {
525                    invalid_indices.push(index);
526                    if index < self.data_shard_count && required[index] {
527                        required_missing_data_indices.push(index);
528                    }
529                } else if valid_indices.len() < self.data_shard_count {
530                    valid_indices.push(index);
531                }
532            }
533
534            if required_missing_data_indices.is_empty() {
535                return Ok(());
536            }
537
538            let data_decode_matrix =
539                self.get_data_decode_matrix(&valid_indices, &invalid_indices)?;
540            let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
541                SmallVec::with_capacity(required_missing_data_indices.len());
542            for &idx in &required_missing_data_indices {
543                matrix_rows.push(data_decode_matrix.get_row(idx));
544            }
545
546            // Borrow present input shards directly and defer output mutation until
547            // after compute completes, so the required-data-only path avoids
548            // cloning every valid shard into temporary owned buffers.
549            let mut sub_shard_ptrs: SmallVec<[(*const F::Elem, usize); 32]> =
550                SmallVec::with_capacity(valid_indices.len());
551            for &idx in &valid_indices {
552                let shard = shards[idx]
553                    .get()
554                    .expect("valid shard index must be present");
555                sub_shard_ptrs.push((shard.as_ptr(), shard.len()));
556            }
557
558            let sub_shards: SmallVec<[&[F::Elem]; 32]> = sub_shard_ptrs
559                .iter()
560                .map(|&(ptr, len)| {
561                    // SAFETY: each pointer/length pair was captured from a present
562                    // shard in `shards` after full length validation. The compute
563                    // phase only reads from these inputs and writes into separate
564                    // owned recovery buffers, so the pointed-to shard storage
565                    // remains valid and unmodified for the duration of these
566                    // borrowed slices.
567                    unsafe { core::slice::from_raw_parts(ptr, len) }
568                })
569                .collect();
570            let mut output_ptrs: SmallVec<[(*mut F::Elem, usize); 32]> =
571                SmallVec::with_capacity(required_missing_data_indices.len());
572            for &idx in &required_missing_data_indices {
573                match shards[idx].get_or_initialize(shard_len) {
574                    Ok(dst) | Err(Ok(dst)) => output_ptrs.push((dst.as_mut_ptr(), dst.len())),
575                    Err(Err(err)) => return Err(err),
576                }
577            }
578            let mut outputs: SmallVec<[&mut [F::Elem]; 32]> = output_ptrs
579                .iter()
580                .map(|&(ptr, len)| {
581                    // SAFETY: each pointer/length pair came from a distinct
582                    // `get_or_initialize(shard_len)` call above after all
583                    // validation completed. Required missing indices are unique,
584                    // and this compute phase mutates only those output buffers.
585                    unsafe { core::slice::from_raw_parts_mut(ptr, len) }
586                })
587                .collect();
588            self.code_some_slices(&matrix_rows, &sub_shards, &mut outputs);
589            drop(outputs);
590        } else {
591            let mut working: Vec<Option<Vec<F::Elem>>> = shards
592                .iter_mut()
593                .map(|shard| shard.get().map(|data| data.to_vec()))
594                .collect();
595            self.reconstruct(&mut working)?;
596
597            for (i, shard) in shards.iter_mut().enumerate() {
598                if !required[i] || !originally_missing[i] {
599                    continue;
600                }
601
602                let recovered = working[i]
603                    .as_ref()
604                    .expect("recovered shard must be present");
605                match shard.get_or_initialize(shard_len) {
606                    Ok(dst) | Err(Ok(dst)) => dst.copy_from_slice(recovered),
607                    Err(Err(err)) => return Err(err),
608                }
609            }
610        }
611
612        Ok(())
613    }
614
615    /// Shared implementation for Leopard GF8/GF16 reconstruction.
616    ///
617    /// Builds the `present`, `outputs`, and `input_data` arrays required by
618    /// the Forney-based FFT decoder, calls the provided `reconstruct_fn`,
619    /// then writes recovered data back into the original shard objects.
620    ///
621    /// Only copies present shard data into output buffers for missing shards
622    /// (the Leopard decoder skips present positions). This avoids the overhead
623    /// of copying all present shard data into temporary buffers.
624    #[allow(clippy::needless_range_loop, clippy::type_complexity)]
625    fn reconstruct_leopard_impl<T: ReconstructShard<F>>(
626        &self,
627        slices: &mut [T],
628        reconstruct_fn: fn(
629            &[bool],
630            &mut [&mut [u8]],
631            &[Option<&[u8]>],
632            usize,
633            usize,
634        ) -> Result<(), Error>,
635    ) -> Result<(), Error> {
636        check_piece_count!(all => self, slices);
637
638        let total = self.total_shard_count;
639        let shard_len_opt: Option<usize> = slices.iter().find_map(|s| s.len());
640        let Some(shard_len) = shard_len_opt else {
641            return Err(Error::EmptyShard);
642        };
643
644        // SAFETY: F::Elem = u8 for all leopard codec families.
645        let mut present = vec![false; total];
646        let mut raw_data: Vec<Option<*const u8>> = vec![None; total];
647        for i in 0..total {
648            if let Some(data) = slices[i].get() {
649                present[i] = true;
650                raw_data[i] = Some(data as *const [F::Elem] as *const u8);
651            }
652        }
653
654        // Allocate output buffers. Present shards get zeroed buffers (the decoder
655        // skips them via `if present[i] { continue; }`). Missing shards also get
656        // zeroed buffers which the decoder writes recovered data into.
657        let mut output_bufs: Vec<Vec<u8>> = (0..total).map(|_| vec![0u8; shard_len]).collect();
658
659        let mut outputs: Vec<&mut [u8]> = output_bufs
660            .iter_mut()
661            .map(|buf| buf.as_mut_slice())
662            .collect();
663
664        let mut input_data: Vec<Option<&[u8]>> = Vec::with_capacity(total);
665        for i in 0..total {
666            if let Some(ptr) = raw_data[i] {
667                // SAFETY: `ptr` was captured from a present shard `slices[i].get()`
668                // whose length equals `shard_len` (all present shards share it);
669                // F::Elem = u8 for leopard, and the shard storage outlives `src`.
670                let src: &[u8] = unsafe { core::slice::from_raw_parts(ptr, shard_len) };
671                input_data.push(Some(src));
672            } else {
673                input_data.push(None);
674            }
675        }
676
677        reconstruct_fn(
678            &present,
679            &mut outputs,
680            &input_data,
681            self.data_shard_count,
682            self.parity_shard_count,
683        )?;
684
685        for i in 0..total {
686            if present[i] {
687                continue;
688            }
689            // SAFETY: F::Elem = u8 for all leopard codec families, so `[u8]` and
690            // `[F::Elem]` have identical layout and the reinterpret cast is valid.
691            let elem_slice: &[F::Elem] =
692                unsafe { &*(output_bufs[i].as_slice() as *const [u8] as *const [F::Elem]) };
693            match slices[i].get_or_initialize(shard_len) {
694                Ok(dst) | Err(Ok(dst)) => dst.copy_from_slice(elem_slice),
695                Err(Err(err)) => return Err(err),
696            }
697        }
698
699        Ok(())
700    }
701
702    fn reconstruct_leopard_gf8<T: ReconstructShard<F>>(
703        &self,
704        slices: &mut [T],
705        _data_only: bool,
706    ) -> Result<(), Error> {
707        self.reconstruct_leopard_impl(slices, super::leopard_gf8::reconstruct_with_tables)
708    }
709
710    fn reconstruct_leopard_gf16<T: ReconstructShard<F>>(
711        &self,
712        slices: &mut [T],
713        _data_only: bool,
714    ) -> Result<(), Error> {
715        self.reconstruct_leopard_impl(slices, super::leopard::leopard_gf16_reconstruct)
716    }
717
718    pub(crate) fn get_data_decode_matrix(
719        &self,
720        valid_indices: &[usize],
721        invalid_indices: &[usize],
722    ) -> Result<Arc<crate::matrix::Matrix<F>>, Error> {
723        if valid_indices.len() != self.data_shard_count {
724            return Err(Error::TooFewShardsPresent);
725        }
726
727        if self.options.inversion_cache {
728            #[cfg(feature = "std")]
729            self.reconstruction_cache_metrics
730                .requests
731                .fetch_add(1, Ordering::Relaxed);
732
733            let mut cache = self.data_decode_matrix_cache.lock();
734            if let Some(entry) = cache.get(invalid_indices) {
735                #[cfg(feature = "std")]
736                self.reconstruction_cache_metrics
737                    .hits
738                    .fetch_add(1, Ordering::Relaxed);
739                return Ok(entry.clone());
740            }
741
742            #[cfg(feature = "std")]
743            self.reconstruction_cache_metrics
744                .misses
745                .fetch_add(1, Ordering::Relaxed);
746        }
747        let mut sub_matrix =
748            crate::matrix::Matrix::new(self.data_shard_count, self.data_shard_count);
749        for (sub_matrix_row, &valid_index) in valid_indices.iter().enumerate() {
750            for c in 0..self.data_shard_count {
751                sub_matrix.set(sub_matrix_row, c, self.matrix.get(valid_index, c));
752            }
753        }
754        let data_decode_matrix = Arc::new(
755            sub_matrix
756                .invert()
757                .map_err(|_| Error::InvalidCustomMatrix)?,
758        );
759        if self.options.inversion_cache {
760            let data_decode_matrix = data_decode_matrix.clone();
761            let mut cache = self.data_decode_matrix_cache.lock();
762            #[cfg(feature = "std")]
763            let before_len = cache.len();
764            #[cfg(feature = "std")]
765            let capacity = cache.capacity();
766            cache.insert(Vec::from(invalid_indices), data_decode_matrix);
767            #[cfg(feature = "std")]
768            if capacity > 0 && before_len >= capacity {
769                self.reconstruction_cache_metrics
770                    .evictions
771                    .fetch_add(1, Ordering::Relaxed);
772            }
773            #[cfg(feature = "std")]
774            self.reconstruction_cache_metrics
775                .inserts
776                .fetch_add(1, Ordering::Relaxed);
777        }
778        Ok(data_decode_matrix)
779    }
780
781    fn reconstruct_internal<T: ReconstructShard<F>>(
782        &self,
783        shards: &mut [T],
784        data_only: bool,
785    ) -> Result<(), Error> {
786        check_piece_count!(all => self, shards);
787
788        let data_shard_count = self.data_shard_count;
789
790        let mut number_present = 0;
791        let mut shard_len = None;
792
793        for shard in shards.iter_mut() {
794            if let Some(len) = shard.len() {
795                if len == 0 {
796                    return Err(Error::EmptyShard);
797                }
798                number_present += 1;
799                if let Some(old_len) = shard_len
800                    && len != old_len
801                {
802                    return Err(Error::IncorrectShardSize);
803                }
804                shard_len = Some(len);
805            }
806        }
807
808        if number_present == self.total_shard_count {
809            #[cfg(feature = "std")]
810            self.runtime_profile_metrics
811                .record_reconstruct(data_only, 0, 0, true);
812            return Ok(());
813        }
814
815        if number_present < data_shard_count {
816            return Err(Error::TooFewShardsPresent);
817        }
818
819        // invariant: reached only when number_present >= data_shard_count >= 1
820        // (see the TooFewDataShards guard in new()), so at least one present
821        // shard has set shard_len to Some.
822        let shard_len = shard_len.expect("at least one shard present; qed");
823
824        let mut sub_shards: SmallVec<[&[F::Elem]; 32]> = SmallVec::with_capacity(data_shard_count);
825        let mut missing_data_slices: SmallVec<[&mut [F::Elem]; 32]> =
826            SmallVec::with_capacity(self.parity_shard_count);
827        let mut missing_parity_slices: SmallVec<[&mut [F::Elem]; 32]> =
828            SmallVec::with_capacity(self.parity_shard_count);
829        let mut valid_indices: SmallVec<[usize; 32]> = SmallVec::with_capacity(data_shard_count);
830        let mut invalid_indices: SmallVec<[usize; 32]> = SmallVec::with_capacity(data_shard_count);
831
832        for (matrix_row, shard) in shards.iter_mut().enumerate() {
833            let shard_data = if matrix_row >= data_shard_count && data_only {
834                shard.get().ok_or(None)
835            } else {
836                shard.get_or_initialize(shard_len).map_err(Some)
837            };
838
839            match shard_data {
840                Ok(shard) => {
841                    if sub_shards.len() < data_shard_count {
842                        sub_shards.push(shard);
843                        valid_indices.push(matrix_row);
844                    }
845                }
846                Err(None) => {
847                    invalid_indices.push(matrix_row);
848                }
849                Err(Some(x)) => {
850                    let shard = x?;
851                    if matrix_row < data_shard_count {
852                        missing_data_slices.push(shard);
853                    } else {
854                        missing_parity_slices.push(shard);
855                    }
856
857                    invalid_indices.push(matrix_row);
858                }
859            }
860        }
861
862        #[cfg(feature = "std")]
863        {
864            let missing_data_count = invalid_indices
865                .iter()
866                .filter(|&&i| i < data_shard_count)
867                .count();
868            let missing_parity_count = if data_only {
869                0
870            } else {
871                invalid_indices
872                    .iter()
873                    .filter(|&&i| i >= data_shard_count)
874                    .count()
875            };
876            self.runtime_profile_metrics.record_reconstruct(
877                data_only,
878                missing_data_count,
879                missing_parity_count,
880                false,
881            );
882        }
883
884        let data_decode_matrix = self.get_data_decode_matrix(&valid_indices, &invalid_indices)?;
885
886        let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
887            SmallVec::with_capacity(self.parity_shard_count);
888
889        for i_slice in invalid_indices
890            .iter()
891            .cloned()
892            .take_while(|i| i < &data_shard_count)
893        {
894            matrix_rows.push(data_decode_matrix.get_row(i_slice));
895        }
896
897        #[cfg(feature = "std")]
898        self.runtime_profile_metrics
899            .record_reconstruct_data_stage(shard_len, matrix_rows.len());
900        self.code_some_slices(&matrix_rows, &sub_shards, &mut missing_data_slices);
901
902        if data_only {
903            Ok(())
904        } else {
905            let mut matrix_rows: SmallVec<[&[F::Elem]; 32]> =
906                SmallVec::with_capacity(self.parity_shard_count);
907            let parity_rows = self.get_parity_rows();
908
909            for i_slice in invalid_indices
910                .iter()
911                .cloned()
912                .skip_while(|i| i < &data_shard_count)
913            {
914                matrix_rows.push(parity_rows[i_slice - data_shard_count]);
915            }
916            #[cfg(feature = "std")]
917            self.runtime_profile_metrics
918                .record_reconstruct_parity_stage(shard_len, matrix_rows.len());
919            {
920                let mut i_old_data_slice = 0;
921                let mut i_new_data_slice = 0;
922
923                let mut all_data_slices: SmallVec<[&[F::Elem]; 32]> =
924                    SmallVec::with_capacity(data_shard_count);
925
926                let mut next_maybe_good = 0;
927                let mut push_good_up_to = move |data_slices: &mut SmallVec<_>, up_to| {
928                    for _ in next_maybe_good..up_to {
929                        data_slices.push(sub_shards[i_old_data_slice]);
930                        i_old_data_slice += 1;
931                    }
932
933                    next_maybe_good = up_to + 1;
934                };
935
936                for i_slice in invalid_indices
937                    .iter()
938                    .cloned()
939                    .take_while(|i| i < &data_shard_count)
940                {
941                    push_good_up_to(&mut all_data_slices, i_slice);
942                    all_data_slices.push(missing_data_slices[i_new_data_slice]);
943                    i_new_data_slice += 1;
944                }
945                push_good_up_to(&mut all_data_slices, data_shard_count);
946
947                self.code_some_slices(&matrix_rows, &all_data_slices, &mut missing_parity_slices);
948            }
949
950            Ok(())
951        }
952    }
953}