1use std::collections::HashMap;
2use std::sync::Mutex;
3
4use crate::errors::{Error, SBSError};
5use crate::galois::{self, EXP_TABLE, LOG_TABLE};
6use crate::matrix::Matrix;
7
8const DATA_DECODE_MATRIX_CACHE_CAPACITY: usize = 254;
9
10#[derive(Debug)]
35pub struct ReedSolomon {
36 data_shard_count: usize,
37 parity_shard_count: usize,
38 total_shard_count: usize,
39 matrix: Matrix,
40 mul_slice_fn: galois::MulSliceFn,
41 mul_slice_xor_fn: galois::MulSliceFn,
42 data_decode_matrix_cache: Mutex<HashMap<Vec<usize>, Matrix>>,
43}
44
45impl Clone for ReedSolomon {
46 fn clone(&self) -> Self {
47 ReedSolomon {
48 data_shard_count: self.data_shard_count,
49 parity_shard_count: self.parity_shard_count,
50 total_shard_count: self.total_shard_count,
51 matrix: self.matrix.clone(),
52 mul_slice_fn: self.mul_slice_fn,
53 mul_slice_xor_fn: self.mul_slice_xor_fn,
54 data_decode_matrix_cache: Mutex::new(HashMap::new()),
55 }
56 }
57}
58
59impl PartialEq for ReedSolomon {
60 fn eq(&self, rhs: &ReedSolomon) -> bool {
61 self.data_shard_count == rhs.data_shard_count
62 && self.parity_shard_count == rhs.parity_shard_count
63 && self.matrix == rhs.matrix
64 }
65}
66
67impl ReedSolomon {
68 fn build_matrix(data_shards: usize, total_shards: usize) -> Matrix {
69 let vandermonde = Matrix::vandermonde(total_shards, data_shards);
70 let top = vandermonde.sub_matrix(0, 0, data_shards, data_shards);
71 let mut result = vandermonde.multiply(&top.invert().unwrap());
72
73 let parity_shards = total_shards - data_shards;
74 let mut inverse = vec![0u8; 256];
75 inverse[0] = 0;
76 inverse[1] = 1;
77 for i in 2..256 {
78 inverse[i] = EXP_TABLE[(255 - LOG_TABLE[i]) as usize];
79 }
80
81 for j in 0..parity_shards {
82 for i in 0..data_shards {
83 result.data[(data_shards + j) * data_shards + i] = inverse[(parity_shards + i) ^ j];
84 }
85 }
86
87 result
88 }
89
90 pub fn new(data_shards: usize, parity_shards: usize) -> Result<Self, Error> {
97 if data_shards == 0 {
98 return Err(Error::TooFewDataShards);
99 }
100 if parity_shards == 0 {
101 return Err(Error::TooFewParityShards);
102 }
103 if data_shards + parity_shards > 256 {
104 return Err(Error::TooManyShards);
105 }
106
107 let total_shards = data_shards + parity_shards;
108 let matrix = Self::build_matrix(data_shards, total_shards);
109 let (mul_slice_fn, mul_slice_xor_fn) = galois::detect_mul_slice();
110
111 Ok(ReedSolomon {
112 data_shard_count: data_shards,
113 parity_shard_count: parity_shards,
114 total_shard_count: total_shards,
115 matrix,
116 mul_slice_fn,
117 mul_slice_xor_fn,
118 data_decode_matrix_cache: Mutex::new(HashMap::new()),
119 })
120 }
121
122 pub fn set_parity_matrix(&mut self, parity: &[u8]) -> Result<(), Error> {
128 let expected = self.parity_shard_count * self.data_shard_count;
129 if parity.len() != expected {
130 return Err(Error::InvalidParityMatrix);
131 }
132 let offset = self.data_shard_count * self.data_shard_count;
133 self.matrix.data[offset..offset + expected].copy_from_slice(parity);
134 self.data_decode_matrix_cache
135 .lock()
136 .unwrap_or_else(|poisoned| poisoned.into_inner())
137 .clear();
138 Ok(())
139 }
140
141 pub fn data_shard_count(&self) -> usize {
142 self.data_shard_count
143 }
144
145 pub fn parity_shard_count(&self) -> usize {
146 self.parity_shard_count
147 }
148
149 pub fn total_shard_count(&self) -> usize {
150 self.total_shard_count
151 }
152
153 #[inline]
154 fn get_parity_rows(&self) -> Vec<&[u8]> {
155 (self.data_shard_count..self.total_shard_count)
156 .map(|i| self.matrix.get_row(i))
157 .collect()
158 }
159
160 #[inline(always)]
162 fn mul_slice(&self, c: u8, input: &[u8], out: &mut [u8]) {
163 assert_eq!(input.len(), out.len());
164 if c == 0 {
165 out.iter_mut().for_each(|o| *o = 0);
166 return;
167 }
168 if c == 1 {
169 out.copy_from_slice(input);
170 return;
171 }
172 (self.mul_slice_fn)(c, input, out);
173 }
174
175 #[inline(always)]
177 fn mul_slice_xor(&self, c: u8, input: &[u8], out: &mut [u8]) {
178 assert_eq!(input.len(), out.len());
179 if c == 0 {
180 return;
181 }
182 if c == 1 {
183 for (o, i) in out.iter_mut().zip(input.iter()) {
184 *o ^= *i;
185 }
186 return;
187 }
188 (self.mul_slice_xor_fn)(c, input, out);
189 }
190
191 fn code_some_slices(&self, matrix_rows: &[&[u8]], inputs: &[&[u8]], outputs: &mut [&mut [u8]]) {
192 for (i_input, input) in inputs.iter().enumerate() {
193 self.code_single_slice(matrix_rows, i_input, input, outputs);
194 }
195 }
196
197 #[inline]
198 fn code_single_slice(
199 &self,
200 matrix_rows: &[&[u8]],
201 i_input: usize,
202 input: &[u8],
203 outputs: &mut [&mut [u8]],
204 ) {
205 for (i_row, output) in outputs.iter_mut().enumerate() {
206 let c = matrix_rows[i_row][i_input];
207 if i_input == 0 {
208 self.mul_slice(c, input, output);
209 } else {
210 self.mul_slice_xor(c, input, output);
211 }
212 }
213 }
214
215 #[cfg(feature = "parallel")]
222 pub fn encode<T: AsRef<[u8]> + AsMut<[u8]> + Send>(
223 &self,
224 shards: &mut [T],
225 ) -> Result<(), Error> {
226 if shards.len() < self.total_shard_count {
227 return Err(Error::TooFewShards);
228 }
229 if shards.len() > self.total_shard_count {
230 return Err(Error::TooManyShards);
231 }
232 Self::check_slices_uniform(shards)?;
233
234 let (data, parity) = shards.split_at_mut(self.data_shard_count);
235 let parity_rows = self.get_parity_rows();
236
237 let shard_size = data[0].as_ref().len();
240 let work = self.parity_shard_count * self.data_shard_count * shard_size;
241 if work > 1_000_000 {
242 use rayon::prelude::*;
243
244 let data_refs: Vec<&[u8]> = data.iter().map(|d| d.as_ref()).collect();
246 let mul_fn = self.mul_slice_fn;
247 let mul_xor_fn = self.mul_slice_xor_fn;
248
249 parity
250 .par_iter_mut()
251 .enumerate()
252 .for_each(|(i_row, p): (usize, &mut T)| {
253 let output = p.as_mut();
254 let row = parity_rows[i_row];
255 for (i_input, &input) in data_refs.iter().enumerate() {
256 let c = row[i_input];
257 if c == 0 {
258 if i_input == 0 {
259 output.iter_mut().for_each(|o| *o = 0);
260 }
261 } else if c == 1 {
262 if i_input == 0 {
263 output.copy_from_slice(input);
264 } else {
265 for (o, i) in output.iter_mut().zip(input.iter()) {
266 *o ^= *i;
267 }
268 }
269 } else if i_input == 0 {
270 mul_fn(c, input, output);
271 } else {
272 mul_xor_fn(c, input, output);
273 }
274 }
275 });
276
277 return Ok(());
278 }
279
280 Self::encode_sequential(
281 data,
282 parity,
283 &parity_rows,
284 self.mul_slice_fn,
285 self.mul_slice_xor_fn,
286 );
287 Ok(())
288 }
289
290 #[cfg(not(feature = "parallel"))]
291 pub fn encode<T: AsRef<[u8]> + AsMut<[u8]>>(&self, shards: &mut [T]) -> Result<(), Error> {
292 if shards.len() < self.total_shard_count {
293 return Err(Error::TooFewShards);
294 }
295 if shards.len() > self.total_shard_count {
296 return Err(Error::TooManyShards);
297 }
298 Self::check_slices_uniform(shards)?;
299
300 let (data, parity) = shards.split_at_mut(self.data_shard_count);
301 let parity_rows = self.get_parity_rows();
302
303 Self::encode_sequential(
304 data,
305 parity,
306 &parity_rows,
307 self.mul_slice_fn,
308 self.mul_slice_xor_fn,
309 );
310 Ok(())
311 }
312
313 fn encode_sequential<T: AsRef<[u8]> + AsMut<[u8]>>(
314 data: &[T],
315 parity: &mut [T],
316 parity_rows: &[&[u8]],
317 mul_slice_fn: galois::MulSliceFn,
318 mul_slice_xor_fn: galois::MulSliceFn,
319 ) {
320 for i_input in 0..data.len() {
321 let input = data[i_input].as_ref();
322 for (i_row, p) in parity.iter_mut().enumerate() {
323 let c = parity_rows[i_row][i_input];
324 let output = p.as_mut();
325 if c == 0 {
326 if i_input == 0 {
327 output.iter_mut().for_each(|o| *o = 0);
328 }
329 } else if c == 1 {
330 if i_input == 0 {
331 output.copy_from_slice(input);
332 } else {
333 for (o, i) in output.iter_mut().zip(input.iter()) {
334 *o ^= *i;
335 }
336 }
337 } else if i_input == 0 {
338 mul_slice_fn(c, input, output);
339 } else {
340 mul_slice_xor_fn(c, input, output);
341 }
342 }
343 }
344 }
345
346 pub fn encode_sep<T: AsRef<[u8]>, U: AsRef<[u8]> + AsMut<[u8]>>(
348 &self,
349 data: &[T],
350 parity: &mut [U],
351 ) -> Result<(), Error> {
352 if data.len() != self.data_shard_count {
353 return Err(if data.len() < self.data_shard_count {
354 Error::TooFewDataShards
355 } else {
356 Error::TooManyDataShards
357 });
358 }
359 if parity.len() != self.parity_shard_count {
360 return Err(if parity.len() < self.parity_shard_count {
361 Error::TooFewParityShards
362 } else {
363 Error::TooManyParityShards
364 });
365 }
366
367 let data_refs: Vec<&[u8]> = data.iter().map(|s| s.as_ref()).collect();
368 let mut parity_refs: Vec<&mut [u8]> = parity.iter_mut().map(|s| s.as_mut()).collect();
369
370 let shard_len = data_refs[0].len();
372 if shard_len == 0 {
373 return Err(Error::EmptyShard);
374 }
375 for d in &data_refs[1..] {
376 if d.len() != shard_len {
377 return Err(Error::IncorrectShardSize);
378 }
379 }
380 for p in parity_refs.iter() {
381 if p.len() != shard_len {
382 return Err(Error::IncorrectShardSize);
383 }
384 }
385
386 let parity_rows = self.get_parity_rows();
387 self.code_some_slices(&parity_rows, &data_refs, &mut parity_refs);
388
389 Ok(())
390 }
391
392 pub fn encode_single<T: AsRef<[u8]> + AsMut<[u8]>>(
398 &self,
399 i_data: usize,
400 shards: &mut [T],
401 ) -> Result<(), Error> {
402 if i_data >= self.data_shard_count {
403 return Err(Error::InvalidIndex);
404 }
405 if shards.len() != self.total_shard_count {
406 return Err(if shards.len() < self.total_shard_count {
407 Error::TooFewShards
408 } else {
409 Error::TooManyShards
410 });
411 }
412 Self::check_slices_uniform(shards)?;
413
414 let (data_part, parity_part) = shards.split_at_mut(self.data_shard_count);
415 let input = data_part[i_data].as_ref();
416 let mut parity_refs: Vec<&mut [u8]> = parity_part.iter_mut().map(|s| s.as_mut()).collect();
417
418 let parity_rows = self.get_parity_rows();
419 self.code_single_slice(&parity_rows, i_data, input, &mut parity_refs);
420
421 Ok(())
422 }
423
424 pub fn encode_single_sep<U: AsRef<[u8]> + AsMut<[u8]>>(
426 &self,
427 i_data: usize,
428 single_data: &[u8],
429 parity: &mut [U],
430 ) -> Result<(), Error> {
431 if i_data >= self.data_shard_count {
432 return Err(Error::InvalidIndex);
433 }
434 if parity.len() != self.parity_shard_count {
435 return Err(if parity.len() < self.parity_shard_count {
436 Error::TooFewParityShards
437 } else {
438 Error::TooManyParityShards
439 });
440 }
441 if single_data.is_empty() {
442 return Err(Error::EmptyShard);
443 }
444 for p in parity.iter() {
445 if p.as_ref().len() != single_data.len() {
446 return Err(Error::IncorrectShardSize);
447 }
448 }
449
450 let mut parity_refs: Vec<&mut [u8]> = parity.iter_mut().map(|s| s.as_mut()).collect();
451 let parity_rows = self.get_parity_rows();
452 self.code_single_slice(&parity_rows, i_data, single_data, &mut parity_refs);
453
454 Ok(())
455 }
456
457 pub fn verify<T: AsRef<[u8]>>(&self, shards: &[T]) -> Result<bool, Error> {
459 if shards.len() != self.total_shard_count {
460 return Err(if shards.len() < self.total_shard_count {
461 Error::TooFewShards
462 } else {
463 Error::TooManyShards
464 });
465 }
466 Self::check_slices_uniform(shards)?;
467
468 let slice_len = shards[0].as_ref().len();
469 let mut buffer: Vec<Vec<u8>> = (0..self.parity_shard_count)
470 .map(|_| vec![0u8; slice_len])
471 .collect();
472
473 let data = &shards[0..self.data_shard_count];
474 let to_check = &shards[self.data_shard_count..];
475
476 let data_refs: Vec<&[u8]> = data.iter().map(|s| s.as_ref()).collect();
477 let mut buf_refs: Vec<&mut [u8]> = buffer.iter_mut().map(|s| s.as_mut_slice()).collect();
478
479 let parity_rows = self.get_parity_rows();
480 self.code_some_slices(&parity_rows, &data_refs, &mut buf_refs);
481
482 for (computed, expected) in buffer.iter().zip(to_check.iter()) {
483 if computed.as_slice() != expected.as_ref() {
484 return Ok(false);
485 }
486 }
487
488 Ok(true)
489 }
490
491 pub fn reconstruct<T: ReconstructShard>(&self, shards: &mut [T]) -> Result<(), Error> {
496 self.reconstruct_internal(shards, false)
497 }
498
499 pub fn reconstruct_data<T: ReconstructShard>(&self, shards: &mut [T]) -> Result<(), Error> {
501 self.reconstruct_internal(shards, true)
502 }
503
504 fn get_data_decode_matrix(
505 &self,
506 valid_indices: &[usize],
507 invalid_indices: &[usize],
508 ) -> Result<Matrix, Error> {
509 {
510 let cache = self
511 .data_decode_matrix_cache
512 .lock()
513 .unwrap_or_else(|poisoned| poisoned.into_inner());
514 if let Some(m) = cache.get(invalid_indices) {
515 return Ok(m.clone());
516 }
517 }
518
519 let mut sub_matrix = Matrix::new(self.data_shard_count, self.data_shard_count);
520 for (sub_row, &valid_index) in valid_indices.iter().enumerate() {
521 for c in 0..self.data_shard_count {
522 sub_matrix.set(sub_row, c, self.matrix.get(valid_index, c));
523 }
524 }
525
526 let data_decode_matrix = sub_matrix.invert().map_err(|_| Error::SingularMatrix)?;
527
528 {
529 let mut cache = self
530 .data_decode_matrix_cache
531 .lock()
532 .unwrap_or_else(|poisoned| poisoned.into_inner());
533 if cache.len() >= DATA_DECODE_MATRIX_CACHE_CAPACITY {
534 cache.clear();
536 }
537 cache.insert(invalid_indices.to_vec(), data_decode_matrix.clone());
538 }
539
540 Ok(data_decode_matrix)
541 }
542
543 fn reconstruct_internal<T: ReconstructShard>(
544 &self,
545 shards: &mut [T],
546 data_only: bool,
547 ) -> Result<(), Error> {
548 if shards.len() != self.total_shard_count {
549 return Err(if shards.len() < self.total_shard_count {
550 Error::TooFewShards
551 } else {
552 Error::TooManyShards
553 });
554 }
555
556 let mut number_present = 0usize;
558 let mut shard_len: Option<usize> = None;
559
560 for shard in shards.iter() {
561 if let Some(len) = shard.len() {
562 if len == 0 {
563 return Err(Error::EmptyShard);
564 }
565 number_present += 1;
566 if let Some(old_len) = shard_len {
567 if len != old_len {
568 return Err(Error::IncorrectShardSize);
569 }
570 }
571 shard_len = Some(len);
572 }
573 }
574
575 if number_present == self.total_shard_count {
576 return Ok(());
577 }
578
579 if number_present < self.data_shard_count {
580 return Err(Error::TooFewShardsPresent);
581 }
582
583 let shard_len = shard_len.expect("at least one shard present");
584
585 let mut valid_indices: Vec<usize> = Vec::with_capacity(self.data_shard_count);
587 let mut invalid_indices: Vec<usize> = Vec::with_capacity(self.parity_shard_count);
588
589 for (i, shard) in shards.iter().enumerate() {
590 if shard.len().is_some() {
591 if valid_indices.len() < self.data_shard_count {
592 valid_indices.push(i);
593 }
594 } else {
595 invalid_indices.push(i);
596 }
597 }
598
599 for &i in &invalid_indices {
601 if i < self.data_shard_count || !data_only {
602 shards[i].initialize(shard_len);
603 }
604 }
605
606 let data_decode_matrix = self.get_data_decode_matrix(&valid_indices, &invalid_indices)?;
607
608 let missing_data_indices: Vec<usize> = invalid_indices
612 .iter()
613 .copied()
614 .filter(|&i| i < self.data_shard_count)
615 .collect();
616
617 if !missing_data_indices.is_empty() {
618 let matrix_rows: Vec<&[u8]> = missing_data_indices
619 .iter()
620 .map(|&i| data_decode_matrix.get_row(i))
621 .collect();
622
623 for (i_input, &valid_idx) in valid_indices.iter().enumerate() {
625 let input_ptr = shards[valid_idx].get().unwrap().as_ptr();
628 let input_slice = unsafe { std::slice::from_raw_parts(input_ptr, shard_len) };
629
630 for (i_out, &missing_idx) in missing_data_indices.iter().enumerate() {
631 let c = matrix_rows[i_out][i_input];
632 let output = shards[missing_idx].get_mut().unwrap();
633 if i_input == 0 {
634 self.mul_slice(c, input_slice, output);
635 } else {
636 self.mul_slice_xor(c, input_slice, output);
637 }
638 }
639 }
640 }
641
642 if data_only {
643 return Ok(());
644 }
645
646 let missing_parity_indices: Vec<usize> = invalid_indices
648 .iter()
649 .copied()
650 .filter(|&i| i >= self.data_shard_count)
651 .collect();
652
653 if !missing_parity_indices.is_empty() {
654 let parity_rows = self.get_parity_rows();
655 let matrix_rows: Vec<&[u8]> = missing_parity_indices
656 .iter()
657 .map(|&i| parity_rows[i - self.data_shard_count])
658 .collect();
659
660 for i_input in 0..self.data_shard_count {
661 let input_ptr = shards[i_input].get().unwrap().as_ptr();
663 let input_slice = unsafe { std::slice::from_raw_parts(input_ptr, shard_len) };
664
665 for (i_out, &missing_idx) in missing_parity_indices.iter().enumerate() {
666 let c = matrix_rows[i_out][i_input];
667 let output = shards[missing_idx].get_mut().unwrap();
668 if i_input == 0 {
669 self.mul_slice(c, input_slice, output);
670 } else {
671 self.mul_slice_xor(c, input_slice, output);
672 }
673 }
674 }
675 }
676
677 Ok(())
678 }
679
680 fn check_slices_uniform<T: AsRef<[u8]>>(slices: &[T]) -> Result<(), Error> {
681 if slices.is_empty() {
682 return Ok(());
683 }
684 let size = slices[0].as_ref().len();
685 if size == 0 {
686 return Err(Error::EmptyShard);
687 }
688 for slice in slices.iter().skip(1) {
689 if slice.as_ref().len() != size {
690 return Err(Error::IncorrectShardSize);
691 }
692 }
693 Ok(())
694 }
695}
696
697pub unsafe trait ReconstructShard {
709 fn len(&self) -> Option<usize>;
711 fn is_empty(&self) -> bool {
713 self.len().is_none()
714 }
715 fn get(&self) -> Option<&[u8]>;
717 fn get_mut(&mut self) -> Option<&mut [u8]>;
719 fn initialize(&mut self, len: usize);
721}
722
723unsafe impl ReconstructShard for Option<Vec<u8>> {
726 fn len(&self) -> Option<usize> {
727 self.as_ref().map(|v| v.len())
728 }
729
730 fn get(&self) -> Option<&[u8]> {
731 self.as_ref().map(|v| v.as_slice())
732 }
733
734 fn get_mut(&mut self) -> Option<&mut [u8]> {
735 self.as_mut().map(|v| v.as_mut_slice())
736 }
737
738 fn initialize(&mut self, len: usize) {
739 if self.is_none() {
740 *self = Some(vec![0u8; len]);
741 }
742 }
743}
744
745pub struct ShardByShard<'a> {
778 codec: &'a ReedSolomon,
779 cur_input: usize,
780}
781
782impl<'a> ShardByShard<'a> {
783 pub fn new(codec: &'a ReedSolomon) -> Self {
784 ShardByShard {
785 codec,
786 cur_input: 0,
787 }
788 }
789
790 pub fn parity_ready(&self) -> bool {
792 self.cur_input == self.codec.data_shard_count
793 }
794
795 pub fn reset(&mut self) -> Result<(), SBSError> {
799 if self.cur_input > 0 && !self.parity_ready() {
800 return Err(SBSError::LeftoverShards);
801 }
802 self.cur_input = 0;
803 Ok(())
804 }
805
806 pub fn reset_force(&mut self) {
808 self.cur_input = 0;
809 }
810
811 pub fn cur_input_index(&self) -> usize {
813 self.cur_input
814 }
815
816 pub fn encode<T: AsRef<[u8]> + AsMut<[u8]>>(
818 &mut self,
819 shards: &mut [T],
820 ) -> Result<(), SBSError> {
821 if self.parity_ready() {
822 return Err(SBSError::TooManyCalls);
823 }
824 self.codec
825 .encode_single(self.cur_input, shards)
826 .map_err(SBSError::RSError)?;
827 self.cur_input += 1;
828 Ok(())
829 }
830
831 pub fn encode_sep<U: AsRef<[u8]> + AsMut<[u8]>>(
833 &mut self,
834 data: &[&[u8]],
835 parity: &mut [U],
836 ) -> Result<(), SBSError> {
837 if self.parity_ready() {
838 return Err(SBSError::TooManyCalls);
839 }
840 if data.len() != self.codec.data_shard_count {
841 return Err(SBSError::RSError(
842 if data.len() < self.codec.data_shard_count {
843 Error::TooFewDataShards
844 } else {
845 Error::TooManyDataShards
846 },
847 ));
848 }
849 self.codec
850 .encode_single_sep(self.cur_input, data[self.cur_input], parity)
851 .map_err(SBSError::RSError)?;
852 self.cur_input += 1;
853 Ok(())
854 }
855}
856
857#[cfg(test)]
858mod tests {
859 use super::*;
860
861 #[test]
862 fn test_new_basic() {
863 let rs = ReedSolomon::new(4, 2).unwrap();
864 assert_eq!(rs.data_shard_count(), 4);
865 assert_eq!(rs.parity_shard_count(), 2);
866 assert_eq!(rs.total_shard_count(), 6);
867 }
868
869 #[test]
870 fn test_new_errors() {
871 assert_eq!(ReedSolomon::new(0, 1), Err(Error::TooFewDataShards));
872 assert_eq!(ReedSolomon::new(1, 0), Err(Error::TooFewParityShards));
873 assert_eq!(ReedSolomon::new(128, 129), Err(Error::TooManyShards));
874 }
875
876 #[test]
877 fn test_set_parity_matrix_rejects_wrong_length() {
878 let mut rs = ReedSolomon::new(4, 2).unwrap();
879 assert_eq!(
880 rs.set_parity_matrix(&[1, 2, 3]),
881 Err(Error::InvalidParityMatrix)
882 );
883 }
884
885 #[test]
886 fn test_partial_eq_reflects_parity_matrix_changes() {
887 let mut lhs = ReedSolomon::new(4, 2).unwrap();
888 let rhs = ReedSolomon::new(4, 2).unwrap();
889
890 assert_eq!(lhs, rhs);
891
892 lhs.set_parity_matrix(&[0x77, 0x40, 0x38, 0x0e, 0xc7, 0xa7, 0x0d, 0x6c])
893 .unwrap();
894
895 assert_ne!(lhs, rhs);
896 }
897
898 #[test]
899 fn test_encode_verify() {
900 let rs = ReedSolomon::new(4, 2).unwrap();
901 let mut shards: Vec<Vec<u8>> = vec![
902 vec![0, 1, 2, 3],
903 vec![4, 5, 6, 7],
904 vec![8, 9, 10, 11],
905 vec![12, 13, 14, 15],
906 vec![0, 0, 0, 0],
907 vec![0, 0, 0, 0],
908 ];
909
910 rs.encode(&mut shards).unwrap();
911 assert!(rs.verify(&shards).unwrap());
912
913 shards[4][0] ^= 0xFF;
915 assert!(!rs.verify(&shards).unwrap());
916 }
917
918 #[test]
919 fn test_reconstruct_missing_data() {
920 let rs = ReedSolomon::new(4, 2).unwrap();
921 let mut shards: Vec<Vec<u8>> = vec![
922 vec![0, 1, 2, 3],
923 vec![4, 5, 6, 7],
924 vec![8, 9, 10, 11],
925 vec![12, 13, 14, 15],
926 vec![0, 0, 0, 0],
927 vec![0, 0, 0, 0],
928 ];
929 rs.encode(&mut shards).unwrap();
930
931 let original = shards.clone();
932
933 let mut recovery: Vec<Option<Vec<u8>>> = shards.into_iter().map(Some).collect();
935 recovery[0] = None;
936
937 rs.reconstruct(&mut recovery).unwrap();
938
939 for (i, shard) in recovery.iter().enumerate() {
940 assert_eq!(shard.as_ref().unwrap(), &original[i]);
941 }
942 }
943
944 #[test]
945 fn test_reconstruct_missing_parity() {
946 let rs = ReedSolomon::new(4, 2).unwrap();
947 let mut shards: Vec<Vec<u8>> = vec![
948 vec![0, 1, 2, 3],
949 vec![4, 5, 6, 7],
950 vec![8, 9, 10, 11],
951 vec![12, 13, 14, 15],
952 vec![0, 0, 0, 0],
953 vec![0, 0, 0, 0],
954 ];
955 rs.encode(&mut shards).unwrap();
956
957 let original = shards.clone();
958
959 let mut recovery: Vec<Option<Vec<u8>>> = shards.into_iter().map(Some).collect();
961 recovery[4] = None;
962 recovery[5] = None;
963
964 rs.reconstruct(&mut recovery).unwrap();
965
966 for (i, shard) in recovery.iter().enumerate() {
967 assert_eq!(shard.as_ref().unwrap(), &original[i]);
968 }
969 }
970
971 #[test]
972 fn test_reconstruct_mixed() {
973 let rs = ReedSolomon::new(4, 2).unwrap();
974 let mut shards: Vec<Vec<u8>> = vec![
975 vec![0, 1, 2, 3],
976 vec![4, 5, 6, 7],
977 vec![8, 9, 10, 11],
978 vec![12, 13, 14, 15],
979 vec![0, 0, 0, 0],
980 vec![0, 0, 0, 0],
981 ];
982 rs.encode(&mut shards).unwrap();
983
984 let original = shards.clone();
985
986 let mut recovery: Vec<Option<Vec<u8>>> = shards.into_iter().map(Some).collect();
988 recovery[1] = None;
989 recovery[4] = None;
990
991 rs.reconstruct(&mut recovery).unwrap();
992
993 for (i, shard) in recovery.iter().enumerate() {
994 assert_eq!(shard.as_ref().unwrap(), &original[i]);
995 }
996 }
997
998 #[test]
999 fn test_reconstruct_too_few_shards() {
1000 let rs = ReedSolomon::new(4, 2).unwrap();
1001 let mut shards: Vec<Vec<u8>> = vec![
1002 vec![0, 1, 2, 3],
1003 vec![4, 5, 6, 7],
1004 vec![8, 9, 10, 11],
1005 vec![12, 13, 14, 15],
1006 vec![0, 0, 0, 0],
1007 vec![0, 0, 0, 0],
1008 ];
1009 rs.encode(&mut shards).unwrap();
1010
1011 let mut recovery: Vec<Option<Vec<u8>>> = shards.into_iter().map(Some).collect();
1012 recovery[0] = None;
1013 recovery[1] = None;
1014 recovery[2] = None;
1015
1016 assert_eq!(
1017 rs.reconstruct(&mut recovery),
1018 Err(Error::TooFewShardsPresent)
1019 );
1020 }
1021
1022 #[test]
1023 fn test_shard_by_shard() {
1024 let rs = ReedSolomon::new(3, 2).unwrap();
1025 let mut sbs = ShardByShard::new(&rs);
1026
1027 let mut shards: Vec<Vec<u8>> = vec![
1028 vec![0, 1, 2, 3, 4],
1029 vec![5, 6, 7, 8, 9],
1030 vec![10, 11, 12, 13, 14],
1031 vec![0, 0, 0, 0, 0],
1032 vec![0, 0, 0, 0, 0],
1033 ];
1034
1035 let mut shards_batch = shards.clone();
1036 rs.encode(&mut shards_batch).unwrap();
1037
1038 sbs.encode(&mut shards).unwrap();
1039 sbs.encode(&mut shards).unwrap();
1040 sbs.encode(&mut shards).unwrap();
1041
1042 assert!(sbs.parity_ready());
1043 assert_eq!(shards, shards_batch);
1044 }
1045
1046 #[test]
1047 fn test_encode_sep() {
1048 let rs = ReedSolomon::new(4, 2).unwrap();
1049 let data: Vec<Vec<u8>> = vec![
1050 vec![0, 1, 2, 3],
1051 vec![4, 5, 6, 7],
1052 vec![8, 9, 10, 11],
1053 vec![12, 13, 14, 15],
1054 ];
1055 let mut parity = vec![vec![0u8; 4]; 2];
1056 rs.encode_sep(&data, &mut parity).unwrap();
1057
1058 let shards: Vec<Vec<u8>> = data.iter().cloned().chain(parity.iter().cloned()).collect();
1060 assert!(rs.verify(&shards).unwrap());
1061
1062 let mut shards2: Vec<Vec<u8>> = data.iter().cloned().chain(vec![vec![0u8; 4]; 2]).collect();
1064 rs.encode(&mut shards2).unwrap();
1065 assert_eq!(shards, shards2);
1066 }
1067
1068 #[test]
1069 fn test_encode_single_sep_matches_batch_encode() {
1070 let rs = ReedSolomon::new(4, 2).unwrap();
1071 let data: Vec<Vec<u8>> = vec![
1072 vec![0, 1, 2, 3],
1073 vec![4, 5, 6, 7],
1074 vec![8, 9, 10, 11],
1075 vec![12, 13, 14, 15],
1076 ];
1077 let mut parity = vec![vec![0u8; 4]; 2];
1078
1079 for (i, shard) in data.iter().enumerate() {
1080 rs.encode_single_sep(i, shard, &mut parity).unwrap();
1081 }
1082
1083 let mut expected: Vec<Vec<u8>> =
1084 data.iter().cloned().chain(vec![vec![0u8; 4]; 2]).collect();
1085 rs.encode(&mut expected).unwrap();
1086
1087 assert_eq!(parity[0], expected[4]);
1088 assert_eq!(parity[1], expected[5]);
1089 }
1090
1091 #[test]
1092 fn test_shard_by_shard_encode_sep_matches_batch_encode() {
1093 let rs = ReedSolomon::new(3, 2).unwrap();
1094 let data: Vec<Vec<u8>> = vec![
1095 vec![0, 1, 2, 3, 4],
1096 vec![5, 6, 7, 8, 9],
1097 vec![10, 11, 12, 13, 14],
1098 ];
1099 let data_refs: Vec<&[u8]> = data.iter().map(Vec::as_slice).collect();
1100 let mut parity = vec![vec![0u8; 5]; 2];
1101 let mut sbs = ShardByShard::new(&rs);
1102
1103 while !sbs.parity_ready() {
1104 sbs.encode_sep(&data_refs, &mut parity).unwrap();
1105 }
1106
1107 let mut expected: Vec<Vec<u8>> =
1108 data.iter().cloned().chain(vec![vec![0u8; 5]; 2]).collect();
1109 rs.encode(&mut expected).unwrap();
1110
1111 assert_eq!(parity[0], expected[3]);
1112 assert_eq!(parity[1], expected[4]);
1113 }
1114
1115 #[test]
1116 fn test_reconstruct_returns_singular_matrix_for_invalid_parity_matrix() {
1117 let mut rs = ReedSolomon::new(4, 2).unwrap();
1118 rs.set_parity_matrix(&[1, 0, 0, 0, 1, 0, 0, 0]).unwrap();
1119
1120 let mut shards: Vec<Vec<u8>> = vec![
1121 vec![0, 1, 2, 3],
1122 vec![4, 5, 6, 7],
1123 vec![8, 9, 10, 11],
1124 vec![12, 13, 14, 15],
1125 vec![0, 0, 0, 0],
1126 vec![0, 0, 0, 0],
1127 ];
1128 rs.encode(&mut shards).unwrap();
1129
1130 let mut recovery: Vec<Option<Vec<u8>>> = shards.into_iter().map(Some).collect();
1131 recovery[0] = None;
1132 recovery[1] = None;
1133
1134 assert_eq!(rs.reconstruct(&mut recovery), Err(Error::SingularMatrix));
1135 }
1136
1137 #[test]
1138 fn test_set_parity_matrix_invalidates_decode_matrix_cache() {
1139 let mut rs = ReedSolomon::new(4, 2).unwrap();
1140 let mut shards: Vec<Vec<u8>> = vec![
1141 vec![0, 1, 2, 3],
1142 vec![4, 5, 6, 7],
1143 vec![8, 9, 10, 11],
1144 vec![12, 13, 14, 15],
1145 vec![0, 0, 0, 0],
1146 vec![0, 0, 0, 0],
1147 ];
1148 rs.encode(&mut shards).unwrap();
1149
1150 let mut initial_recovery: Vec<Option<Vec<u8>>> = shards.iter().cloned().map(Some).collect();
1151 initial_recovery[0] = None;
1152 initial_recovery[1] = None;
1153 rs.reconstruct(&mut initial_recovery).unwrap();
1154
1155 rs.set_parity_matrix(&[1, 0, 0, 0, 1, 0, 0, 0]).unwrap();
1156
1157 let mut recovery_after_update: Vec<Option<Vec<u8>>> =
1158 shards.into_iter().map(Some).collect();
1159 recovery_after_update[0] = None;
1160 recovery_after_update[1] = None;
1161
1162 assert_eq!(
1163 rs.reconstruct(&mut recovery_after_update),
1164 Err(Error::SingularMatrix)
1165 );
1166 }
1167
1168 #[cfg(feature = "parallel")]
1169 #[test]
1170 fn test_parallel_encode_matches_sequential_path() {
1171 let rs = ReedSolomon::new(4, 2).unwrap();
1172 let shard_len = 200_000;
1173 let mut shards: Vec<Vec<u8>> = (0..6)
1174 .map(|i| {
1175 if i < 4 {
1176 (0..shard_len).map(|j| ((i * 17 + j) % 251) as u8).collect()
1177 } else {
1178 vec![0u8; shard_len]
1179 }
1180 })
1181 .collect();
1182
1183 let parity_rows = rs.get_parity_rows();
1184 let mut expected = vec![vec![0u8; shard_len]; 2];
1185
1186 {
1187 let (data, _) = shards.split_at_mut(4);
1188 ReedSolomon::encode_sequential(
1189 data,
1190 &mut expected,
1191 &parity_rows,
1192 rs.mul_slice_fn,
1193 rs.mul_slice_xor_fn,
1194 );
1195 }
1196
1197 rs.encode(&mut shards).unwrap();
1198
1199 assert_eq!(shards[4], expected[0]);
1200 assert_eq!(shards[5], expected[1]);
1201 }
1202
1203 #[test]
1204 fn test_various_shard_counts() {
1205 for data in [1, 2, 5, 10, 50, 127] {
1206 for parity in [1, 2, 3, 5] {
1207 if data + parity > 256 {
1208 continue;
1209 }
1210 let rs = ReedSolomon::new(data, parity).unwrap();
1211 let mut shards: Vec<Vec<u8>> = (0..data + parity)
1212 .map(|i| {
1213 if i < data {
1214 vec![i as u8; 64]
1215 } else {
1216 vec![0u8; 64]
1217 }
1218 })
1219 .collect();
1220
1221 rs.encode(&mut shards).unwrap();
1222 assert!(
1223 rs.verify(&shards).unwrap(),
1224 "Verify failed for data={data}, parity={parity}"
1225 );
1226
1227 let original = shards.clone();
1229 let mut recovery: Vec<Option<Vec<u8>>> = shards.into_iter().map(Some).collect();
1230 recovery[0] = None;
1231 rs.reconstruct(&mut recovery).unwrap();
1232 assert_eq!(
1233 recovery[0].as_ref().unwrap(),
1234 &original[0],
1235 "Reconstruct failed for data={data}, parity={parity}"
1236 );
1237 }
1238 }
1239 }
1240
1241 #[test]
1242 fn test_reconstruct_data_only() {
1243 let rs = ReedSolomon::new(4, 2).unwrap();
1244 let mut shards: Vec<Vec<u8>> = vec![
1245 vec![0, 1, 2, 3],
1246 vec![4, 5, 6, 7],
1247 vec![8, 9, 10, 11],
1248 vec![12, 13, 14, 15],
1249 vec![0, 0, 0, 0],
1250 vec![0, 0, 0, 0],
1251 ];
1252 rs.encode(&mut shards).unwrap();
1253
1254 let original_data = shards[0].clone();
1255
1256 let mut recovery: Vec<Option<Vec<u8>>> = shards.into_iter().map(Some).collect();
1257 recovery[0] = None;
1258
1259 rs.reconstruct_data(&mut recovery).unwrap();
1260
1261 assert_eq!(recovery[0].as_ref().unwrap(), &original_data);
1262 }
1263}