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 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 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
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 _parity_refs.push(unsafe {
314 core::slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len())
315 });
316 }
317
318 #[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 #[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 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 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[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}