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        // SAFETY: only called when size_of::<F::Elem>() == 1, so F::Elem is u8.
301        // u8 and F::Elem share layout (align=1, size=1), so the pointer cast is valid.
302        let mut _parity_u8: smallvec::SmallVec<[&mut [u8]; 32]> = parity
303            .iter_mut()
304            .map(|p| unsafe { &mut *(p.as_mut() as *mut [F::Elem] as *mut [u8]) })
305            .collect();
306        let parity_rows = self.get_parity_rows();
307        let mut _parity_refs: smallvec::SmallVec<[&[u8]; 32]> =
308            smallvec::SmallVec::with_capacity(parity_rows.len());
309        for r in parity_rows.iter() {
310            let slice: &[F::Elem] = r;
311            // SAFETY: only called when size_of::<F::Elem>() == 1, so F::Elem is u8;
312            // ptr/len come from a live `&[F::Elem]`, valid for `slice.len()` bytes.
313            _parity_refs.push(unsafe {
314                core::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len())
315            });
316        }
317
318        // x86_64 AVX2 codegen path
319        #[cfg(all(
320            feature = "simd-avx2",
321            target_arch = "x86_64",
322            not(target_env = "msvc"),
323            not(any(target_os = "android", target_os = "ios"))
324        ))]
325        {
326            if crate::galois_8::x86::codegen::try_encode_codegen_avx2(
327                self.data_shard_count,
328                self.parity_shard_count,
329                &_parity_refs,
330                &_data_u8,
331                &mut _parity_u8,
332                _shard_len,
333            ) {
334                return true;
335            }
336        }
337
338        // aarch64 NEON codegen path
339        #[cfg(all(
340            feature = "simd-neon",
341            target_arch = "aarch64",
342            not(target_env = "msvc"),
343            not(any(target_os = "android", target_os = "ios"))
344        ))]
345        {
346            if crate::galois_8::aarch64::codegen::try_encode_codegen_neon(
347                self.data_shard_count,
348                self.parity_shard_count,
349                &_parity_refs,
350                &_data_u8,
351                &mut _parity_u8,
352                _shard_len,
353            ) {
354                return true;
355            }
356        }
357
358        false
359    }
360
361    pub(crate) fn encode_fast_one_parity<
362        T: AsRef<[F::Elem]>,
363        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
364    >(
365        &self,
366        data: &[T],
367        parity: &mut [U],
368    ) {
369        let output = parity[0].as_mut();
370        output.copy_from_slice(data[0].as_ref());
371        for input in &data[1..] {
372            for (out, value) in output.iter_mut().zip(input.as_ref().iter()) {
373                *out = F::add(*out, *value);
374            }
375        }
376    }
377
378    /// Encode one data shard into all parity shards.
379    ///
380    /// Not supported for Leopard codec families.
381    pub fn encode_single<T, U>(&self, i_data: usize, mut shards: T) -> Result<(), Error>
382    where
383        T: AsRef<[U]> + AsMut<[U]>,
384        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
385    {
386        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
387            return Err(Error::UnsupportedCodecFamily);
388        }
389        let slices = shards.as_mut();
390
391        check_slice_index!(data => self, i_data);
392        check_piece_count!(all=> self, slices);
393        check_slices!(multi => slices);
394
395        let (mut_input, output) = slices.split_at_mut(self.data_shard_count);
396        let input = mut_input[i_data].as_ref();
397
398        self.encode_single_sep(i_data, input, output)
399    }
400
401    /// Encode one data shard into separate parity slices.
402    pub fn encode_single_sep<U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
403        &self,
404        i_data: usize,
405        single_data: &[F::Elem],
406        parity: &mut [U],
407    ) -> Result<(), Error> {
408        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
409            return Err(Error::UnsupportedCodecFamily);
410        }
411        check_slice_index!(data => self, i_data);
412        check_piece_count!(parity => self, parity);
413        check_slices!(multi => parity, single => single_data);
414
415        let parity_rows = self.get_parity_rows();
416        self.code_single_slice(&parity_rows, i_data, single_data, parity);
417
418        Ok(())
419    }
420
421    /// Encode data shards in-place, filling parity shards.
422    ///
423    /// The first `data_shard_count` slices are data (read-only), the rest are parity (written).
424    pub fn encode<T, U>(&self, mut shards: T) -> Result<(), Error>
425    where
426        T: AsRef<[U]> + AsMut<[U]>,
427        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
428    {
429        let slices: &mut [U] = shards.as_mut();
430
431        check_piece_count!(all => self, slices);
432        check_slices!(multi => slices);
433
434        let (input, output) = slices.split_at_mut(self.data_shard_count);
435        self.encode_sep(&*input, output)
436    }
437
438    /// Encode from separate data and parity slices.
439    pub fn encode_sep<T: AsRef<[F::Elem]>, U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
440        &self,
441        data: &[T],
442        parity: &mut [U],
443    ) -> Result<(), Error> {
444        check_piece_count!(data => self, data);
445        check_piece_count!(parity => self, parity);
446        check_slices!(multi => data, multi => parity);
447
448        if self.is_leopard_gf8_family() {
449            return self.encode_leopard_gf8_sep(data, parity);
450        }
451        if self.is_leopard_gf16_family() {
452            return self.encode_leopard_gf16_sep(data, parity);
453        }
454
455        if self.fast_one_parity_enabled() {
456            self.encode_fast_one_parity(data, parity);
457            return Ok(());
458        }
459
460        // Try SIMD codegen path for GF(2^8) with common configurations.
461        if core::mem::size_of::<F::Elem>() == 1 {
462            let shard_len = data.first().map(|d| d.as_ref().len()).unwrap_or(0);
463            if shard_len > 0 && self.try_encode_codegen(data, parity, shard_len) {
464                return Ok(());
465            }
466        }
467
468        let parity_rows = self.get_parity_rows();
469        self.code_some_slices(&parity_rows, data, parity);
470
471        Ok(())
472    }
473
474    pub(crate) fn encode_leopard_gf8_sep<
475        T: AsRef<[F::Elem]>,
476        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
477    >(
478        &self,
479        data: &[T],
480        parity: &mut [U],
481    ) -> Result<(), Error> {
482        self.encode_leopard_sep_inner(data, parity, leopard::leopard_gf8_encode)
483    }
484
485    pub(crate) fn encode_leopard_gf16_sep<
486        T: AsRef<[F::Elem]>,
487        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
488    >(
489        &self,
490        data: &[T],
491        parity: &mut [U],
492    ) -> Result<(), Error> {
493        self.encode_leopard_sep_inner(data, parity, leopard::leopard_gf16_encode)
494    }
495
496    #[allow(clippy::type_complexity)]
497    fn encode_leopard_sep_inner<T: AsRef<[F::Elem]>, U: AsRef<[F::Elem]> + AsMut<[F::Elem]>>(
498        &self,
499        data: &[T],
500        parity: &mut [U],
501        encode_fn: fn(usize, usize, &[&[u8]], &mut [&mut [u8]]) -> Result<(), Error>,
502    ) -> Result<(), Error> {
503        let data_u8: Vec<&[u8]> = data
504            .iter()
505            .map(|s| {
506                let slice: &[F::Elem] = s.as_ref();
507                // SAFETY: Leopard is only instantiated when F::Elem = u8.
508                unsafe { &*(slice as *const [F::Elem] as *const [u8]) }
509            })
510            .collect();
511        let mut parity_u8: Vec<&mut [u8]> = parity
512            .iter_mut()
513            .map(|s| {
514                let slice: &mut [F::Elem] = s.as_mut();
515                // SAFETY: Same as above — F::Elem = u8 for leopard.
516                unsafe { &mut *(slice as *mut [F::Elem] as *mut [u8]) }
517            })
518            .collect();
519        encode_fn(
520            self.data_shard_count,
521            self.parity_shard_count,
522            &data_u8,
523            &mut parity_u8,
524        )
525    }
526
527    /// Incrementally update parity shards when some data shards change.
528    ///
529    /// `old_data` contains the previous data shards; `new_data` contains `Some(new)` for
530    /// changed shards and `None` for unchanged ones. Not supported for Leopard families.
531    pub fn update<T, U>(
532        &self,
533        old_data: &[T],
534        new_data: &[Option<T>],
535        parity: &mut [U],
536    ) -> Result<(), Error>
537    where
538        T: AsRef<[F::Elem]>,
539        U: AsRef<[F::Elem]> + AsMut<[F::Elem]>,
540    {
541        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
542            return Err(Error::UnsupportedCodecFamily);
543        }
544        self.ensure_classic_family_execution()?;
545        check_piece_count!(data => self, old_data);
546        check_piece_count!(parity => self, parity);
547
548        if new_data.len() != self.data_shard_count {
549            return Err(Error::TooFewDataShards);
550        }
551
552        check_slices!(multi => old_data, multi => parity);
553
554        let shard_len = old_data
555            .first()
556            .map(|shard| shard.as_ref().len())
557            .ok_or(Error::TooFewDataShards)?;
558        if shard_len == 0 {
559            return Err(Error::EmptyShard);
560        }
561
562        for new_shard in new_data.iter().flatten() {
563            if new_shard.as_ref().len() != shard_len {
564                return Err(Error::IncorrectShardSize);
565            }
566        }
567
568        if self.fast_one_parity_enabled() {
569            let parity = parity[0].as_mut();
570            for (old, new) in old_data.iter().zip(new_data.iter()) {
571                let Some(new) = new.as_ref() else {
572                    continue;
573                };
574
575                for ((dst, old_byte), new_byte) in parity
576                    .iter_mut()
577                    .zip(old.as_ref().iter())
578                    .zip(new.as_ref().iter())
579                {
580                    *dst = F::add(*dst, F::add(*old_byte, *new_byte));
581                }
582            }
583            return Ok(());
584        }
585
586        let parity_rows = self.get_parity_rows();
587        let mut delta = vec![F::zero(); shard_len];
588
589        for (i_data, (old, new)) in old_data.iter().zip(new_data.iter()).enumerate() {
590            let Some(new) = new.as_ref() else {
591                continue;
592            };
593
594            let old = old.as_ref();
595            let new = new.as_ref();
596            for (slot, (old_elem, new_elem)) in delta.iter_mut().zip(old.iter().zip(new.iter())) {
597                *slot = F::add(*old_elem, *new_elem);
598            }
599            self.update_parity_with_delta(&parity_rows, i_data, &delta, parity);
600        }
601
602        Ok(())
603    }
604
605    /// Parallel version of [`encode_sep`](Self::encode_sep).
606    #[cfg(feature = "std")]
607    pub fn encode_sep_par<T, U>(&self, data: &[T], parity: &mut [U]) -> Result<(), Error>
608    where
609        F::Elem: Send + Sync,
610        T: AsRef<[F::Elem]> + Sync,
611        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
612    {
613        check_piece_count!(data => self, data);
614        check_piece_count!(parity => self, parity);
615        check_slices!(multi => data, multi => parity);
616
617        if self.is_leopard_gf8_family() {
618            return self.encode_leopard_gf8_sep(data, parity);
619        }
620        if self.is_leopard_gf16_family() {
621            return self.encode_leopard_gf16_sep(data, parity);
622        }
623
624        if self.fast_one_parity_enabled() {
625            self.encode_fast_one_parity(data, parity);
626            return Ok(());
627        }
628
629        let parity_rows = self.get_parity_rows();
630        let shard_len = data[0].as_ref().len();
631        let decision = self.parallel_policy(shard_len, parity.len());
632        if !decision.use_parallel {
633            self.code_some_slices(&parity_rows, data, parity);
634            return Ok(());
635        }
636        self.code_some_slices_par_chunked(&parity_rows, data, parity, decision.chunk_len);
637
638        Ok(())
639    }
640
641    /// Parallel version of [`encode_single_sep`](Self::encode_single_sep).
642    #[cfg(feature = "std")]
643    pub fn encode_single_sep_par<U>(
644        &self,
645        i_data: usize,
646        single_data: &[F::Elem],
647        parity: &mut [U],
648    ) -> Result<(), Error>
649    where
650        F::Elem: Send + Sync,
651        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
652    {
653        if self.is_leopard_gf8_family() || self.is_leopard_gf16_family() {
654            return Err(Error::UnsupportedCodecFamily);
655        }
656        check_slice_index!(data => self, i_data);
657        check_piece_count!(parity => self, parity);
658        check_slices!(multi => parity, single => single_data);
659
660        let parity_rows = self.get_parity_rows();
661        let decision = self.parallel_policy(single_data.len(), parity.len());
662        if !decision.use_parallel {
663            self.code_single_slice(&parity_rows, i_data, single_data, parity);
664            return Ok(());
665        }
666        self.code_single_slice_par_chunked(
667            &parity_rows,
668            i_data,
669            single_data,
670            parity,
671            decision.chunk_len,
672        );
673
674        Ok(())
675    }
676
677    /// Auto-parallelizing version of [`encode_single_sep`](Self::encode_single_sep).
678    ///
679    /// Uses the parallel policy to choose between serial and parallel execution.
680    #[cfg(feature = "std")]
681    pub fn encode_single_sep_opt<U>(
682        &self,
683        i_data: usize,
684        single_data: &[F::Elem],
685        parity: &mut [U],
686    ) -> Result<(), Error>
687    where
688        F::Elem: Send + Sync,
689        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
690    {
691        let decision = self.parallel_policy(single_data.len(), parity.len());
692        if decision.use_parallel {
693            self.encode_single_sep_par(i_data, single_data, parity)
694        } else {
695            self.encode_single_sep(i_data, single_data, parity)
696        }
697    }
698
699    /// Auto-parallelizing version of [`encode_single`](Self::encode_single).
700    #[cfg(feature = "std")]
701    pub fn encode_single_opt<T, U>(&self, i_data: usize, mut shards: T) -> Result<(), Error>
702    where
703        F::Elem: Send + Sync,
704        T: AsRef<[U]> + AsMut<[U]>,
705        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send,
706    {
707        let slices = shards.as_mut();
708
709        check_slice_index!(data => self, i_data);
710        check_piece_count!(all=> self, slices);
711        check_slices!(multi => slices);
712
713        let (mut_input, output) = slices.split_at_mut(self.data_shard_count);
714        let input = mut_input[i_data].as_ref();
715        let decision = self.parallel_policy(input.len(), output.len());
716        let parity_rows = self.get_parity_rows();
717        if decision.use_parallel {
718            self.code_single_slice_par_chunked(
719                &parity_rows,
720                i_data,
721                input,
722                output,
723                decision.chunk_len,
724            );
725        } else {
726            self.code_single_slice(&parity_rows, i_data, input, output);
727        }
728        Ok(())
729    }
730
731    /// Parallel version of [`encode`](Self::encode).
732    #[cfg(feature = "std")]
733    pub fn encode_par<T, U>(&self, mut shards: T) -> Result<(), Error>
734    where
735        F::Elem: Send + Sync,
736        T: AsRef<[U]> + AsMut<[U]>,
737        U: AsRef<[F::Elem]> + AsMut<[F::Elem]> + Send + Sync,
738    {
739        let slices: &mut [U] = shards.as_mut();
740
741        check_piece_count!(all => self, slices);
742        check_slices!(multi => slices);
743
744        let (input, output) = slices.split_at_mut(self.data_shard_count);
745        self.encode_sep_par(&*input, output)
746    }
747}