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
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 #[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 #[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 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 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[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}