Skip to main content

rustfs_erasure_codec/core/
encode.rs

1extern crate alloc;
2
3#[cfg(feature = "std")]
4use rayon::prelude::*;
5
6use alloc::vec;
7use alloc::vec::Vec;
8
9use crate::Field;
10use crate::errors::Error;
11
12use super::{
13    CODE_SLICE_DEFAULT_CHUNK_BYTES, CODE_SLICE_LARGE_CHUNK_BYTES, CODE_SLICE_MIN_CHUNK_BYTES,
14    ReedSolomon, leopard,
15};
16
17impl<F: Field> ReedSolomon<F> {
18    pub(crate) fn code_some_slices<T: AsRef<[F::Elem]>, U: AsMut<[F::Elem]>>(
19        &self,
20        matrix_rows: &[&[F::Elem]],
21        inputs: &[T],
22        outputs: &mut [U],
23    ) {
24        self.code_some_slices_chunked(matrix_rows, inputs, outputs);
25    }
26
27    pub(crate) fn code_some_slices_chunked<T: AsRef<[F::Elem]>, U: AsMut<[F::Elem]>>(
28        &self,
29        matrix_rows: &[&[F::Elem]],
30        inputs: &[T],
31        outputs: &mut [U],
32    ) {
33        let shard_len = inputs
34            .first()
35            .map(|input| input.as_ref().len())
36            .unwrap_or(0);
37        if shard_len == 0 {
38            return;
39        }
40
41        let chunk_len = self.code_chunk_len(shard_len);
42        #[cfg(feature = "std")]
43        self.runtime_profile_metrics.record_code_some(
44            false,
45            shard_len,
46            inputs.len(),
47            outputs.len(),
48            chunk_len,
49        );
50        let mut start = 0;
51        while start < shard_len {
52            let end = core::cmp::min(start + chunk_len, shard_len);
53            for (i_input, input) in inputs.iter().enumerate().take(self.data_shard_count) {
54                self.code_single_slice_range(
55                    matrix_rows,
56                    i_input,
57                    input.as_ref(),
58                    outputs,
59                    start,
60                    end,
61                );
62            }
63            start = end;
64        }
65    }
66
67    pub(crate) fn code_chunk_len(&self, shard_len: usize) -> usize {
68        let chunk = Self::serial_code_chunk_len(shard_len);
69
70        core::cmp::min(chunk, shard_len)
71    }
72
73    fn serial_code_chunk_len(shard_len: usize) -> usize {
74        if shard_len <= CODE_SLICE_MIN_CHUNK_BYTES {
75            shard_len
76        } else if shard_len <= CODE_SLICE_DEFAULT_CHUNK_BYTES {
77            CODE_SLICE_MIN_CHUNK_BYTES
78        } else if shard_len <= 4 * 1024 * 1024 {
79            CODE_SLICE_DEFAULT_CHUNK_BYTES
80        } else {
81            CODE_SLICE_LARGE_CHUNK_BYTES
82        }
83    }
84
85    #[cfg(feature = "std")]
86    pub(crate) fn code_some_slices_par_chunked<T, U>(
87        &self,
88        matrix_rows: &[&[F::Elem]],
89        inputs: &[T],
90        outputs: &mut [U],
91        chunk_len: usize,
92    ) where
93        F::Elem: Send + Sync,
94        T: AsRef<[F::Elem]> + Sync,
95        U: AsMut<[F::Elem]> + Send,
96    {
97        let shard_len = inputs
98            .first()
99            .map(|input| input.as_ref().len())
100            .unwrap_or(0);
101        if shard_len == 0 {
102            return;
103        }
104
105        self.runtime_profile_metrics.record_code_some(
106            true,
107            shard_len,
108            inputs.len(),
109            outputs.len(),
110            chunk_len,
111        );
112        let data_shard_count = self.data_shard_count;
113        let chunk_count = shard_len.div_ceil(chunk_len);
114        if outputs.len() <= 2 && chunk_count > 1 {
115            self.runtime_profile_metrics
116                .record_code_some_small_output_chunk_parallel(outputs.len(), chunk_count);
117            if outputs.len() == 1 {
118                let matrix_row = matrix_rows[0];
119                outputs[0]
120                    .as_mut()
121                    .par_chunks_mut(chunk_len)
122                    .enumerate()
123                    .for_each(|(chunk_idx, output_chunk)| {
124                        let start = chunk_idx * chunk_len;
125                        let end = start + output_chunk.len();
126
127                        F::mul_slice(matrix_row[0], &inputs[0].as_ref()[start..end], output_chunk);
128                        for i_input in 1..data_shard_count {
129                            F::mul_slice_add(
130                                matrix_row[i_input],
131                                &inputs[i_input].as_ref()[start..end],
132                                output_chunk,
133                            );
134                        }
135                    });
136            } else {
137                let matrix_row0 = matrix_rows[0];
138                let matrix_row1 = matrix_rows[1];
139                let (first, second) = outputs.split_at_mut(1);
140                let output0 = first[0].as_mut();
141                let output1 = second[0].as_mut();
142
143                output0
144                    .par_chunks_mut(chunk_len)
145                    .zip(output1.par_chunks_mut(chunk_len))
146                    .enumerate()
147                    .for_each(|(chunk_idx, (output0_chunk, output1_chunk))| {
148                        let start = chunk_idx * chunk_len;
149                        let end = start + output0_chunk.len();
150                        let input0 = &inputs[0].as_ref()[start..end];
151
152                        F::mul_slice(matrix_row0[0], input0, output0_chunk);
153                        F::mul_slice(matrix_row1[0], input0, output1_chunk);
154                        for i_input in 1..data_shard_count {
155                            let input_chunk = &inputs[i_input].as_ref()[start..end];
156                            F::mul_slice_add(matrix_row0[i_input], input_chunk, output0_chunk);
157                            F::mul_slice_add(matrix_row1[i_input], input_chunk, output1_chunk);
158                        }
159                    });
160            }
161        } else {
162            outputs
163                .par_iter_mut()
164                .enumerate()
165                .for_each(|(i_row, output)| {
166                    let matrix_row = matrix_rows[i_row];
167                    let output = output.as_mut();
168
169                    let mut start = 0;
170                    while start < shard_len {
171                        let end = core::cmp::min(start + chunk_len, shard_len);
172                        let output_chunk = &mut output[start..end];
173
174                        F::mul_slice(matrix_row[0], &inputs[0].as_ref()[start..end], output_chunk);
175                        for i_input in 1..data_shard_count {
176                            F::mul_slice_add(
177                                matrix_row[i_input],
178                                &inputs[i_input].as_ref()[start..end],
179                                output_chunk,
180                            );
181                        }
182
183                        start = end;
184                    }
185                });
186        }
187    }
188
189    #[cfg(feature = "std")]
190    fn code_single_slice_par_chunked<U: AsMut<[F::Elem]> + Send>(
191        &self,
192        matrix_rows: &[&[F::Elem]],
193        i_input: usize,
194        input: &[F::Elem],
195        outputs: &mut [U],
196        chunk_len: usize,
197    ) where
198        F::Elem: Send + Sync,
199    {
200        let shard_len = input.len();
201        if shard_len == 0 {
202            return;
203        }
204
205        self.runtime_profile_metrics
206            .record_code_single(true, shard_len, outputs.len(), chunk_len);
207        outputs
208            .par_iter_mut()
209            .enumerate()
210            .for_each(|(i_row, output)| {
211                let coefficient = matrix_rows[i_row][i_input];
212                let output = output.as_mut();
213
214                let mut start = 0;
215                while start < shard_len {
216                    let end = core::cmp::min(start + chunk_len, shard_len);
217                    let output_chunk = &mut output[start..end];
218                    let input_chunk = &input[start..end];
219                    if i_input == 0 {
220                        F::mul_slice(coefficient, input_chunk, output_chunk);
221                    } else {
222                        F::mul_slice_add(coefficient, input_chunk, output_chunk);
223                    }
224                    start = end;
225                }
226            });
227    }
228
229    pub(crate) fn code_single_slice_range<U: AsMut<[F::Elem]>>(
230        &self,
231        matrix_rows: &[&[F::Elem]],
232        i_input: usize,
233        input: &[F::Elem],
234        outputs: &mut [U],
235        start: usize,
236        end: usize,
237    ) {
238        let input = &input[start..end];
239        outputs.iter_mut().enumerate().for_each(|(i_row, output)| {
240            let matrix_row_to_use = matrix_rows[i_row][i_input];
241            let output = &mut output.as_mut()[start..end];
242
243            if i_input == 0 {
244                F::mul_slice(matrix_row_to_use, input, output);
245            } else {
246                F::mul_slice_add(matrix_row_to_use, input, output);
247            }
248        })
249    }
250
251    pub(crate) fn code_single_slice<U: AsMut<[F::Elem]>>(
252        &self,
253        matrix_rows: &[&[F::Elem]],
254        i_input: usize,
255        input: &[F::Elem],
256        outputs: &mut [U],
257    ) {
258        #[cfg(feature = "std")]
259        self.runtime_profile_metrics.record_code_single(
260            false,
261            input.len(),
262            outputs.len(),
263            input.len(),
264        );
265        self.code_single_slice_range(matrix_rows, i_input, input, outputs, 0, input.len());
266    }
267
268    fn update_parity_with_delta<U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
269        &self,
270        matrix_rows: &[&[F::Elem]],
271        i_input: usize,
272        delta: &[F::Elem],
273        outputs: &mut [U],
274    ) {
275        outputs.iter_mut().enumerate().for_each(|(i_row, output)| {
276            let coefficient = matrix_rows[i_row][i_input];
277            F::mul_slice_add(coefficient, delta, output.as_mut());
278        });
279    }
280
281    pub(crate) fn fast_one_parity_enabled(&self) -> bool {
282        self.options.fast_one_parity && self.parity_shard_count == 1
283    }
284
285    /// Attempt SIMD codegen encode for GF(2^8) with common configurations.
286    /// Returns `true` if the codegen path was used, `false` to fall back to generic path.
287    fn try_encode_codegen<T: AsRef<[F::Elem]>, U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
288        &self,
289        data: &[T],
290        parity: &mut [U],
291        _shard_len: usize,
292    ) -> bool {
293        // SAFETY: We only call this when size_of::<F::Elem>() == 1, so F::Elem is u8.
294        // u8 and F::Elem have identical layout (align=1, size=1), so pointer casts are valid.
295        let _data_u8: smallvec::SmallVec<[&[u8]; 32]> = data
296            .iter()
297            .map(|d| unsafe { &*(d.as_ref() as *const [F::Elem] as *const [u8]) })
298            .collect();
299        let _parity_len = parity.len();
300        let mut _parity_u8: smallvec::SmallVec<[&mut [u8]; 32]> = parity
301            .iter_mut()
302            .map(|p| unsafe { &mut *(p.as_mut() as *mut [F::Elem] as *mut [u8]) })
303            .collect();
304        let parity_rows = self.get_parity_rows();
305        let mut _parity_refs: smallvec::SmallVec<[&[u8]; 32]> =
306            smallvec::SmallVec::with_capacity(parity_rows.len());
307        for r in parity_rows.iter() {
308            let slice: &[F::Elem] = r;
309            _parity_refs.push(unsafe {
310                core::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len())
311            });
312        }
313
314        // x86_64 AVX2 codegen path
315        #[cfg(all(
316            feature = "simd-avx2",
317            target_arch = "x86_64",
318            not(target_env = "msvc"),
319            not(any(target_os = "android", target_os = "ios"))
320        ))]
321        {
322            if crate::galois_8::x86::codegen::try_encode_codegen_avx2(
323                self.data_shard_count,
324                self.parity_shard_count,
325                &_parity_refs,
326                &_data_u8,
327                &mut _parity_u8,
328                _shard_len,
329            ) {
330                return true;
331            }
332        }
333
334        // aarch64 NEON codegen path
335        #[cfg(all(
336            feature = "simd-neon",
337            target_arch = "aarch64",
338            not(target_env = "msvc"),
339            not(any(target_os = "android", target_os = "ios"))
340        ))]
341        {
342            if crate::galois_8::aarch64::codegen::try_encode_codegen_neon(
343                self.data_shard_count,
344                self.parity_shard_count,
345                &_parity_refs,
346                &_data_u8,
347                &mut _parity_u8,
348                _shard_len,
349            ) {
350                return true;
351            }
352        }
353
354        false
355    }
356
357    pub(crate) fn encode_fast_one_parity<
358        T: AsRef<[F::Elem]>,
359        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
360    >(
361        &self,
362        data: &[T],
363        parity: &mut [U],
364    ) {
365        let output = parity[0].as_mut();
366        output.copy_from_slice(data[0].as_ref());
367        for input in &data[1..] {
368            for (out, value) in output.iter_mut().zip(input.as_ref().iter()) {
369                *out = F::add(*out, *value);
370            }
371        }
372    }
373
374    /// Encode one data shard into all parity shards.
375    ///
376    /// Not supported for Leopard codec families.
377    pub fn encode_single<T, U>(&self, i_data: usize, mut shards: T) -> Result<(), Error>
378    where
379        T: AsRef<[U]> + AsMut<[U]>,
380        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
381    {
382        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
383            return Err(Error::UnsupportedCodecFamily);
384        }
385        let slices = shards.as_mut();
386
387        check_slice_index!(data => self, i_data);
388        check_piece_count!(all=> self, slices);
389        check_slices!(multi => slices);
390
391        let (mut_input, output) = slices.split_at_mut(self.data_shard_count);
392        let input = mut_input[i_data].as_ref();
393
394        self.encode_single_sep(i_data, input, output)
395    }
396
397    /// Encode one data shard into separate parity slices.
398    pub fn encode_single_sep<U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
399        &self,
400        i_data: usize,
401        single_data: &[F::Elem],
402        parity: &mut [U],
403    ) -> Result<(), Error> {
404        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
405            return Err(Error::UnsupportedCodecFamily);
406        }
407        check_slice_index!(data => self, i_data);
408        check_piece_count!(parity => self, parity);
409        check_slices!(multi => parity, single => single_data);
410
411        let parity_rows = self.get_parity_rows();
412        self.code_single_slice(&parity_rows, i_data, single_data, parity);
413
414        Ok(())
415    }
416
417    /// Encode data shards in-place, filling parity shards.
418    ///
419    /// The first `data_shard_count` slices are data (read-only), the rest are parity (written).
420    pub fn encode<T, U>(&self, mut shards: T) -> Result<(), Error>
421    where
422        T: AsRef<[U]> + AsMut<[U]>,
423        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
424    {
425        let slices: &mut [U] = shards.as_mut();
426
427        check_piece_count!(all => self, slices);
428        check_slices!(multi => slices);
429
430        let (input, output) = slices.split_at_mut(self.data_shard_count);
431        self.encode_sep(&*input, output)
432    }
433
434    /// Encode from separate data and parity slices.
435    pub fn encode_sep<T: AsRef<[F::Elem]>, U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
436        &self,
437        data: &[T],
438        parity: &mut [U],
439    ) -> Result<(), Error> {
440        check_piece_count!(data => self, data);
441        check_piece_count!(parity => self, parity);
442        check_slices!(multi => data, multi => parity);
443
444        if self.is_leopard_gf8_family() {
445            return self.encode_leopard_gf8_sep(data, parity);
446        }
447        if self.is_leopard_gf16_family() {
448            return self.encode_leopard_gf16_sep(data, parity);
449        }
450
451        if self.fast_one_parity_enabled() {
452            self.encode_fast_one_parity(data, parity);
453            return Ok(());
454        }
455
456        // Try SIMD codegen path for GF(2^8) with common configurations.
457        if core::mem::size_of::<F::Elem>() == 1 {
458            let shard_len = data.first().map(|d| d.as_ref().len()).unwrap_or(0);
459            if shard_len > 0 && self.try_encode_codegen(data, parity, shard_len) {
460                return Ok(());
461            }
462        }
463
464        let parity_rows = self.get_parity_rows();
465        self.code_some_slices(&parity_rows, data, parity);
466
467        Ok(())
468    }
469
470    pub(crate) fn encode_leopard_gf8_sep<
471        T: AsRef<[F::Elem]>,
472        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
473    >(
474        &self,
475        data: &[T],
476        parity: &mut [U],
477    ) -> Result<(), Error> {
478        self.encode_leopard_sep_inner(data, parity, leopard::leopard_gf8_encode)
479    }
480
481    pub(crate) fn encode_leopard_gf16_sep<
482        T: AsRef<[F::Elem]>,
483        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
484    >(
485        &self,
486        data: &[T],
487        parity: &mut [U],
488    ) -> Result<(), Error> {
489        self.encode_leopard_sep_inner(data, parity, leopard::leopard_gf16_encode)
490    }
491
492    #[allow(clippy::type_complexity)]
493    fn encode_leopard_sep_inner<T: AsRef<[F::Elem]>, U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
494        &self,
495        data: &[T],
496        parity: &mut [U],
497        encode_fn: fn(usize, usize, &[&[u8]], &mut [&mut [u8]]) -> Result<(), Error>,
498    ) -> Result<(), Error> {
499        let data_u8: Vec<&[u8]> = data
500            .iter()
501            .map(|s| {
502                let slice: &[F::Elem] = s.as_ref();
503                // SAFETY: Leopard is only instantiated when F::Elem = u8.
504                unsafe { &*(slice as *const [F::Elem] as *const [u8]) }
505            })
506            .collect();
507        let mut parity_u8: Vec<&mut [u8]> = parity
508            .iter_mut()
509            .map(|s| {
510                let slice: &mut [F::Elem] = s.as_mut();
511                // SAFETY: Same as above — F::Elem = u8 for leopard.
512                unsafe { &mut *(slice as *mut [F::Elem] as *mut [u8]) }
513            })
514            .collect();
515        encode_fn(
516            self.data_shard_count,
517            self.parity_shard_count,
518            &data_u8,
519            &mut parity_u8,
520        )
521    }
522
523    /// Incrementally update parity shards when some data shards change.
524    ///
525    /// `old_data` contains the previous data shards; `new_data` contains `Some(new)` for
526    /// changed shards and `None` for unchanged ones. Not supported for Leopard families.
527    pub fn update<T, U>(
528        &self,
529        old_data: &[T],
530        new_data: &[Option<T>],
531        parity: &mut [U],
532    ) -> Result<(), Error>
533    where
534        T: AsRef<[F::Elem]>,
535        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
536    {
537        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
538            return Err(Error::UnsupportedCodecFamily);
539        }
540        self.ensure_classic_family_execution()?;
541        check_piece_count!(data => self, old_data);
542        check_piece_count!(parity => self, parity);
543
544        if new_data.len() != self.data_shard_count {
545            return Err(Error::TooFewDataShards);
546        }
547
548        check_slices!(multi => old_data, multi => parity);
549
550        let shard_len = old_data
551            .first()
552            .map(|shard| shard.as_ref().len())
553            .ok_or(Error::TooFewDataShards)?;
554        if shard_len == 0 {
555            return Err(Error::EmptyShard);
556        }
557
558        for new_shard in new_data.iter().flatten() {
559            if new_shard.as_ref().len() != shard_len {
560                return Err(Error::IncorrectShardSize);
561            }
562        }
563
564        if self.fast_one_parity_enabled() {
565            let parity = parity[0].as_mut();
566            for (old, new) in old_data.iter().zip(new_data.iter()) {
567                let Some(new) = new.as_ref() else {
568                    continue;
569                };
570
571                for ((dst, old_byte), new_byte) in parity
572                    .iter_mut()
573                    .zip(old.as_ref().iter())
574                    .zip(new.as_ref().iter())
575                {
576                    *dst = F::add(*dst, F::add(*old_byte, *new_byte));
577                }
578            }
579            return Ok(());
580        }
581
582        let parity_rows = self.get_parity_rows();
583        let mut delta = vec![F::zero(); shard_len];
584
585        for (i_data, (old, new)) in old_data.iter().zip(new_data.iter()).enumerate() {
586            let Some(new) = new.as_ref() else {
587                continue;
588            };
589
590            let old = old.as_ref();
591            let new = new.as_ref();
592            for (slot, (old_elem, new_elem)) in delta.iter_mut().zip(old.iter().zip(new.iter())) {
593                *slot = F::add(*old_elem, *new_elem);
594            }
595            self.update_parity_with_delta(&parity_rows, i_data, &delta, parity);
596        }
597
598        Ok(())
599    }
600
601    /// Parallel version of [`encode_sep`](Self::encode_sep).
602    #[cfg(feature = "std")]
603    pub fn encode_sep_par<T, U>(&self, data: &[T], parity: &mut [U]) -> Result<(), Error>
604    where
605        F::Elem: Send + Sync,
606        T: AsRef<[F::Elem]> + Sync,
607        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
608    {
609        check_piece_count!(data => self, data);
610        check_piece_count!(parity => self, parity);
611        check_slices!(multi => data, multi => parity);
612
613        if self.is_leopard_gf8_family() {
614            return self.encode_leopard_gf8_sep(data, parity);
615        }
616        if self.is_leopard_gf16_family() {
617            return self.encode_leopard_gf16_sep(data, parity);
618        }
619
620        if self.fast_one_parity_enabled() {
621            self.encode_fast_one_parity(data, parity);
622            return Ok(());
623        }
624
625        let parity_rows = self.get_parity_rows();
626        let shard_len = data[0].as_ref().len();
627        let decision = self.parallel_policy(shard_len, parity.len());
628        if !decision.use_parallel {
629            self.code_some_slices(&parity_rows, data, parity);
630            return Ok(());
631        }
632        self.code_some_slices_par_chunked(&parity_rows, data, parity, decision.chunk_len);
633
634        Ok(())
635    }
636
637    /// Parallel version of [`encode_single_sep`](Self::encode_single_sep).
638    #[cfg(feature = "std")]
639    pub fn encode_single_sep_par<U>(
640        &self,
641        i_data: usize,
642        single_data: &[F::Elem],
643        parity: &mut [U],
644    ) -> Result<(), Error>
645    where
646        F::Elem: Send + Sync,
647        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
648    {
649        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
650            return Err(Error::UnsupportedCodecFamily);
651        }
652        check_slice_index!(data => self, i_data);
653        check_piece_count!(parity => self, parity);
654        check_slices!(multi => parity, single => single_data);
655
656        let parity_rows = self.get_parity_rows();
657        let decision = self.parallel_policy(single_data.len(), parity.len());
658        if !decision.use_parallel {
659            self.code_single_slice(&parity_rows, i_data, single_data, parity);
660            return Ok(());
661        }
662        self.code_single_slice_par_chunked(
663            &parity_rows,
664            i_data,
665            single_data,
666            parity,
667            decision.chunk_len,
668        );
669
670        Ok(())
671    }
672
673    /// Auto-parallelizing version of [`encode_single_sep`](Self::encode_single_sep).
674    ///
675    /// Uses the parallel policy to choose between serial and parallel execution.
676    #[cfg(feature = "std")]
677    pub fn encode_single_sep_opt<U>(
678        &self,
679        i_data: usize,
680        single_data: &[F::Elem],
681        parity: &mut [U],
682    ) -> Result<(), Error>
683    where
684        F::Elem: Send + Sync,
685        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
686    {
687        let decision = self.parallel_policy(single_data.len(), parity.len());
688        if decision.use_parallel {
689            self.encode_single_sep_par(i_data, single_data, parity)
690        } else {
691            self.encode_single_sep(i_data, single_data, parity)
692        }
693    }
694
695    /// Auto-parallelizing version of [`encode_single`](Self::encode_single).
696    #[cfg(feature = "std")]
697    pub fn encode_single_opt<T, U>(&self, i_data: usize, mut shards: T) -> Result<(), Error>
698    where
699        F::Elem: Send + Sync,
700        T: AsRef<[U]> + AsMut<[U]>,
701        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
702    {
703        let slices = shards.as_mut();
704
705        check_slice_index!(data => self, i_data);
706        check_piece_count!(all=> self, slices);
707        check_slices!(multi => slices);
708
709        let (mut_input, output) = slices.split_at_mut(self.data_shard_count);
710        let input = mut_input[i_data].as_ref();
711        let decision = self.parallel_policy(input.len(), output.len());
712        let parity_rows = self.get_parity_rows();
713        if decision.use_parallel {
714            self.code_single_slice_par_chunked(
715                &parity_rows,
716                i_data,
717                input,
718                output,
719                decision.chunk_len,
720            );
721        } else {
722            self.code_single_slice(&parity_rows, i_data, input, output);
723        }
724        Ok(())
725    }
726
727    /// Parallel version of [`encode`](Self::encode).
728    #[cfg(feature = "std")]
729    pub fn encode_par<T, U>(&self, mut shards: T) -> Result<(), Error>
730    where
731        F::Elem: Send + Sync,
732        T: AsRef<[U]> + AsMut<[U]>,
733        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send + Sync,
734    {
735        let slices: &mut [U] = shards.as_mut();
736
737        check_piece_count!(all => self, slices);
738        check_slices!(multi => slices);
739
740        let (input, output) = slices.split_at_mut(self.data_shard_count);
741        self.encode_sep_par(&*input, output)
742    }
743}