1use std::io::{Error, Read};
31use std::mem;
32
33#[derive(Debug, Clone)]
39pub struct StreamOptions {
40 pub block_size: usize,
45}
46
47impl Default for StreamOptions {
48 fn default() -> Self {
49 Self {
50 block_size: 4 * 1024 * 1024,
51 }
52 }
53}
54
55impl StreamOptions {
56 pub fn new() -> Self {
58 Self::default()
59 }
60
61 pub fn with_block_size(mut self, size: usize) -> Self {
63 const MIN_BLOCK_SIZE_BYTES: usize = 1024;
64 const MAX_BLOCK_SIZE_BYTES: usize = 16 * 1024 * 1024;
65 self.block_size = size.clamp(MIN_BLOCK_SIZE_BYTES, MAX_BLOCK_SIZE_BYTES);
66 self
67 }
68}
69
70#[derive(Debug)]
76pub struct StreamError {
77 pub shard_index: usize,
79 pub kind: StreamErrorKind,
81}
82
83#[derive(Debug)]
85pub enum StreamErrorKind {
86 Read(std::io::Error),
88 Write(std::io::Error),
90 Codec(crate::Error),
92}
93
94impl core::fmt::Display for StreamError {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 match &self.kind {
97 StreamErrorKind::Read(e) => {
98 write!(f, "read error on shard {}: {}", self.shard_index, e)
99 }
100 StreamErrorKind::Write(e) => {
101 write!(f, "write error on shard {}: {}", self.shard_index, e)
102 }
103 StreamErrorKind::Codec(e) => {
104 write!(f, "codec error on shard {}: {}", self.shard_index, e)
105 }
106 }
107 }
108}
109
110impl std::error::Error for StreamError {
111 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
112 match &self.kind {
113 StreamErrorKind::Read(e) => Some(e),
114 StreamErrorKind::Write(e) => Some(e),
115 StreamErrorKind::Codec(e) => Some(e),
116 }
117 }
118}
119
120impl StreamError {
121 fn read(shard_index: usize, e: std::io::Error) -> Self {
122 Self {
123 shard_index,
124 kind: StreamErrorKind::Read(e),
125 }
126 }
127
128 fn write(shard_index: usize, e: std::io::Error) -> Self {
129 Self {
130 shard_index,
131 kind: StreamErrorKind::Write(e),
132 }
133 }
134
135 fn codec(shard_index: usize, e: crate::Error) -> Self {
136 Self {
137 shard_index,
138 kind: StreamErrorKind::Codec(e),
139 }
140 }
141}
142
143fn take_stream_error(
144 first_error: &std::sync::Mutex<Option<StreamError>>,
145 fallback_message: &'static str,
146) -> StreamError {
147 match first_error.lock() {
148 Ok(mut guard) => guard
149 .take()
150 .unwrap_or_else(|| StreamError::read(0, Error::other(fallback_message))),
151 Err(poisoned) => {
152 let mut guard = poisoned.into_inner();
153 guard
154 .take()
155 .unwrap_or_else(|| StreamError::read(0, Error::other(fallback_message)))
156 }
157 }
158}
159
160fn read_block<R: std::io::Read>(
170 readers: &mut [R],
171 buffers: &mut [Vec<u8>],
172 max_len: usize,
173) -> Result<(bool, usize), StreamError> {
174 let mut any_data = false;
175 let mut actual_len = 0;
176
177 for (i, (reader, buf)) in readers.iter_mut().zip(buffers.iter_mut()).enumerate() {
178 buf.resize(max_len, 0);
179 let mut total = 0;
180
181 while total < max_len {
182 match reader.read(&mut buf[total..]) {
183 Ok(0) => break,
184 Ok(n) => {
185 total += n;
186 any_data = true;
187 }
188 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
189 Err(e) => return Err(StreamError::read(i, e)),
190 }
191 }
192
193 if total > actual_len {
194 actual_len = total;
195 }
196
197 for byte in &mut buf[total..] {
199 *byte = 0;
200 }
201 }
202
203 Ok((!any_data, actual_len))
204}
205
206fn read_block_all<R: std::io::Read>(
208 readers: &mut [R],
209 buffers: &mut [Vec<u8>],
210 max_len: usize,
211) -> Result<(bool, usize), StreamError> {
212 let mut any_data = false;
213 let mut actual_len = 0;
214
215 for (i, (reader, buf)) in readers.iter_mut().zip(buffers.iter_mut()).enumerate() {
216 buf.resize(max_len, 0);
217 let mut total = 0;
218
219 while total < max_len {
220 match reader.read(&mut buf[total..]) {
221 Ok(0) => break,
222 Ok(n) => {
223 total += n;
224 any_data = true;
225 }
226 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
227 Err(e) => return Err(StreamError::read(i, e)),
228 }
229 }
230
231 if total > actual_len {
232 actual_len = total;
233 }
234
235 for byte in &mut buf[total..] {
236 *byte = 0;
237 }
238 }
239
240 Ok((!any_data, actual_len))
241}
242
243fn write_block<W: std::io::Write>(
245 writers: &mut [W],
246 buffers: &[Vec<u8>],
247 len: usize,
248 shard_offset: usize,
249) -> Result<(), StreamError> {
250 for (i, (writer, buf)) in writers.iter_mut().zip(buffers.iter()).enumerate() {
251 let mut written = 0;
252 while written < len {
253 match writer.write(&buf[written..len]) {
254 Ok(0) => {
255 return Err(StreamError::write(
256 shard_offset + i,
257 std::io::Error::new(std::io::ErrorKind::WriteZero, "write returned 0"),
258 ));
259 }
260 Ok(n) => written += n,
261 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
262 Err(e) => return Err(StreamError::write(shard_offset + i, e)),
263 }
264 }
265 }
266 Ok(())
267}
268
269fn read_block_par<R: std::io::Read + Send>(
275 readers: &mut [R],
276 buffers: &mut [Vec<u8>],
277 max_len: usize,
278) -> Result<(bool, usize), StreamError> {
279 use rayon::prelude::*;
280 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
281
282 let any_data = AtomicBool::new(false);
283 let actual_len = AtomicUsize::new(0);
284 let first_error: std::sync::Mutex<Option<StreamError>> = std::sync::Mutex::new(None);
285
286 readers
287 .par_iter_mut()
288 .zip(buffers.par_iter_mut())
289 .enumerate()
290 .try_for_each(|(i, (reader, buf))| {
291 buf.resize(max_len, 0);
292 let mut total = 0;
293
294 while total < max_len {
295 match reader.read(&mut buf[total..]) {
296 Ok(0) => break,
297 Ok(n) => {
298 total += n;
299 any_data.store(true, Ordering::Relaxed);
300 }
301 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
302 Err(e) => {
303 if let Ok(mut guard) = first_error.lock()
304 && guard.is_none()
305 {
306 *guard = Some(StreamError::read(i, e));
307 }
308 return Err(());
309 }
310 }
311 }
312
313 actual_len.fetch_max(total, Ordering::Relaxed);
314 for byte in &mut buf[total..] {
315 *byte = 0;
316 }
317 Ok(())
318 })
319 .map_err(|()| {
320 take_stream_error(
321 &first_error,
322 "parallel stream reader error was not reported",
323 )
324 })?;
325
326 Ok((
327 !any_data.load(Ordering::Relaxed),
328 actual_len.load(Ordering::Relaxed),
329 ))
330}
331
332fn read_block_all_par<R: std::io::Read + Send>(
334 readers: &mut [R],
335 buffers: &mut [Vec<u8>],
336 max_len: usize,
337) -> Result<(bool, usize), StreamError> {
338 read_block_par(readers, buffers, max_len)
339}
340
341fn write_block_par<W: std::io::Write + Send>(
343 writers: &mut [W],
344 buffers: &[Vec<u8>],
345 len: usize,
346 shard_offset: usize,
347) -> Result<(), StreamError> {
348 use rayon::prelude::*;
349
350 let first_error: std::sync::Mutex<Option<StreamError>> = std::sync::Mutex::new(None);
351
352 writers
353 .par_iter_mut()
354 .zip(buffers.par_iter())
355 .enumerate()
356 .try_for_each(|(i, (writer, buf))| {
357 let mut written = 0;
358 while written < len {
359 match writer.write(&buf[written..len]) {
360 Ok(0) => {
361 if let Ok(mut guard) = first_error.lock()
362 && guard.is_none()
363 {
364 *guard = Some(StreamError::write(
365 shard_offset + i,
366 std::io::Error::new(
367 std::io::ErrorKind::WriteZero,
368 "write returned 0",
369 ),
370 ));
371 }
372 return Err(());
373 }
374 Ok(n) => written += n,
375 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
376 Err(e) => {
377 if let Ok(mut guard) = first_error.lock()
378 && guard.is_none()
379 {
380 *guard = Some(StreamError::write(shard_offset + i, e));
381 }
382 return Err(());
383 }
384 }
385 }
386 Ok(())
387 })
388 .map_err(|()| {
389 take_stream_error(
390 &first_error,
391 "parallel stream writer error was not reported",
392 )
393 })
394}
395
396impl super::ReedSolomon<crate::galois_8::Field> {
401 pub fn encode_stream(
411 &self,
412 data: &mut [impl std::io::Read + Send],
413 parity: &mut [impl std::io::Write + Send],
414 options: &StreamOptions,
415 ) -> Result<(), StreamError> {
416 let block_size = options.block_size;
417 let data_count = self.data_shard_count;
418 let parity_count = self.parity_shard_count;
419
420 debug_assert_eq!(data.len(), data_count);
421 debug_assert_eq!(parity.len(), parity_count);
422
423 let mut data_bufs: Vec<Vec<u8>> = (0..data_count)
424 .map(|_| Vec::with_capacity(block_size))
425 .collect();
426 let mut parity_bufs: Vec<Vec<u8>> = (0..parity_count)
427 .map(|_| Vec::with_capacity(block_size))
428 .collect();
429
430 loop {
431 let (all_eof, actual_len) = read_block_par(data, &mut data_bufs, block_size)?;
432 if all_eof {
433 break;
434 }
435
436 for buf in parity_bufs.iter_mut() {
438 buf.resize(actual_len, 0);
439 }
440
441 let data_refs: Vec<&[u8]> = data_bufs.iter().map(|b| &b[..actual_len]).collect();
443 let mut parity_refs: Vec<&mut [u8]> = parity_bufs
444 .iter_mut()
445 .map(|b| &mut b[..actual_len])
446 .collect();
447
448 self.encode_sep_par(&data_refs, &mut parity_refs)
449 .map_err(|e| StreamError::codec(0, e))?;
450
451 write_block_par(parity, &parity_bufs, actual_len, data_count)?;
453 }
454
455 Ok(())
456 }
457
458 pub fn verify_stream(
464 &self,
465 shards: &mut [impl std::io::Read + Send],
466 options: &StreamOptions,
467 ) -> Result<bool, StreamError> {
468 let block_size = options.block_size;
469 let total = self.total_shard_count;
470
471 debug_assert_eq!(shards.len(), total);
472
473 let mut bufs: Vec<Vec<u8>> = (0..total).map(|_| Vec::with_capacity(block_size)).collect();
474
475 loop {
476 let (all_eof, actual_len) = read_block_all_par(shards, &mut bufs, block_size)?;
477 if all_eof {
478 break;
479 }
480
481 let refs: Vec<&[u8]> = bufs.iter().map(|b| &b[..actual_len]).collect();
482
483 let valid = self
484 .verify_par(&refs)
485 .map_err(|e| StreamError::codec(0, e))?;
486 if !valid {
487 return Ok(false);
488 }
489 }
490
491 Ok(true)
492 }
493
494 pub fn reconstruct_stream(
510 &self,
511 shards: &mut [std::io::Cursor<Vec<u8>>],
512 options: &StreamOptions,
513 ) -> Result<(), StreamError> {
514 let block_size = options.block_size;
515 let total = self.total_shard_count;
516
517 debug_assert_eq!(shards.len(), total);
518
519 let mut present = vec![false; total];
521 for (i, shard) in shards.iter().enumerate() {
522 present[i] = !shard.get_ref().is_empty();
523 }
524
525 let missing_count = present.iter().filter(|&&p| !p).count();
526 if missing_count > self.parity_shard_count {
527 return Err(StreamError::codec(0, crate::Error::TooFewShardsPresent));
528 }
529
530 let mut bufs: Vec<Vec<u8>> = (0..total)
535 .map(|i| {
536 if present[i] {
537 Vec::with_capacity(block_size)
538 } else {
539 Vec::new()
540 }
541 })
542 .collect();
543
544 loop {
545 use rayon::prelude::*;
547 use std::sync::atomic::{AtomicBool, Ordering};
548
549 let any_data = AtomicBool::new(false);
550 let first_error: std::sync::Mutex<Option<StreamError>> = std::sync::Mutex::new(None);
551
552 #[allow(clippy::type_complexity)]
553 let mut indexed: Vec<(usize, &mut std::io::Cursor<Vec<u8>>, &mut Vec<u8>)> = shards
554 .iter_mut()
555 .zip(bufs.iter_mut())
556 .enumerate()
557 .filter(|(i, _)| present[*i])
558 .map(|(i, (s, b))| (i, s, b))
559 .collect();
560
561 indexed
562 .par_iter_mut()
563 .try_for_each(|(i, shard, buf)| {
564 buf.resize(block_size, 0);
565 let mut total_read = 0;
566 while total_read < block_size {
567 match shard.read(&mut buf[total_read..]) {
568 Ok(0) => break,
569 Ok(n) => {
570 total_read += n;
571 any_data.store(true, Ordering::Relaxed);
572 }
573 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
574 Err(e) => {
575 if let Ok(mut guard) = first_error.lock()
576 && guard.is_none()
577 {
578 *guard = Some(StreamError::read(*i, e));
579 }
580 return Err(());
581 }
582 }
583 }
584 buf.truncate(total_read);
585 Ok(())
586 })
587 .map_err(|()| {
588 take_stream_error(
589 &first_error,
590 "parallel stream reader error was not reported",
591 )
592 })?;
593
594 if !any_data.load(Ordering::Relaxed) {
595 break;
596 }
597
598 let actual_len = bufs
600 .iter()
601 .enumerate()
602 .filter(|(idx, _)| present[*idx])
603 .find_map(|(_, b)| if b.is_empty() { None } else { Some(b.len()) })
604 .unwrap_or(0);
605 if actual_len == 0 {
606 break;
607 }
608
609 for buf in bufs.iter_mut() {
611 buf.resize(actual_len, 0);
612 }
613
614 let mut reconstruct_bufs: Vec<Option<Vec<u8>>> = (0..total)
615 .map(|i| {
616 if present[i] {
617 Some(mem::take(&mut bufs[i]))
618 } else {
619 None
620 }
621 })
622 .collect();
623
624 self.reconstruct(&mut reconstruct_bufs)
625 .map_err(|e| StreamError::codec(0, e))?;
626
627 for (i, shard) in shards.iter_mut().enumerate() {
630 if !present[i] {
631 let recovered = reconstruct_bufs[i].as_ref().expect("missing shard buffer");
632 shard.get_mut().extend_from_slice(&recovered[..actual_len]);
633 }
634 }
635 }
636
637 Ok(())
638 }
639}
640
641#[cfg(test)]
642mod tests {
643 use super::*;
644 use crate::galois_8::ReedSolomon;
645
646 fn make_codec(data: usize, parity: usize) -> ReedSolomon {
647 ReedSolomon::new(data, parity).unwrap()
648 }
649
650 fn random_data(len: usize) -> Vec<u8> {
651 (0..len)
653 .map(|i| (i.wrapping_mul(73).wrapping_add(17)) as u8)
654 .collect()
655 }
656
657 #[test]
658 fn test_encode_stream_basic() {
659 let rs = make_codec(3, 2);
660 let shard_size = 4096;
661
662 let d0 = random_data(shard_size);
663 let d1 = random_data(shard_size);
664 let d2 = random_data(shard_size);
665
666 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice(), d2.as_slice()];
667 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
668
669 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
670 .unwrap();
671
672 assert_eq!(writers[0].len(), shard_size);
673 assert_eq!(writers[1].len(), shard_size);
674
675 let all: Vec<&[u8]> = vec![
677 d0.as_slice(),
678 d1.as_slice(),
679 d2.as_slice(),
680 writers[0].as_slice(),
681 writers[1].as_slice(),
682 ];
683 assert!(rs.verify(&all).unwrap());
684 }
685
686 #[test]
687 fn test_encode_stream_multi_block() {
688 let rs = make_codec(3, 2);
689 let total_size = 10 * 1024; let block_size = 4096;
691
692 let d0 = random_data(total_size);
693 let d1 = random_data(total_size);
694 let d2 = random_data(total_size);
695
696 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice(), d2.as_slice()];
697 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
698
699 let opts = StreamOptions::new().with_block_size(block_size);
700 rs.encode_stream(&mut readers, &mut writers, &opts).unwrap();
701
702 assert_eq!(writers[0].len(), total_size);
703 assert_eq!(writers[1].len(), total_size);
704 }
705
706 #[test]
707 fn test_encode_stream_empty() {
708 let rs = make_codec(3, 2);
709 let empty: Vec<&[u8]> = vec![&[], &[], &[]];
710 let mut readers = empty.clone();
711 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
712
713 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
714 .unwrap();
715
716 assert!(writers[0].is_empty());
718 assert!(writers[1].is_empty());
719 }
720
721 #[test]
722 fn test_encode_stream_unequal_lengths() {
723 let rs = make_codec(3, 2);
724
725 let d0 = random_data(1000);
726 let d1 = random_data(500);
727 let d2 = random_data(800);
728
729 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice(), d2.as_slice()];
730 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
731
732 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
733 .unwrap();
734
735 assert_eq!(writers[0].len(), 1000);
738 assert_eq!(writers[1].len(), 1000);
739 }
740
741 #[test]
742 fn test_encode_stream_10x4() {
743 let rs = make_codec(10, 4);
744 let shard_size = 64 * 1024;
745
746 let data: Vec<Vec<u8>> = (0..10).map(|_| random_data(shard_size)).collect();
747 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
748 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 4];
749
750 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
751 .unwrap();
752
753 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
755 for w in &writers {
756 all.push(w.as_slice());
757 }
758 assert!(rs.verify(&all).unwrap());
759 }
760
761 #[test]
762 fn test_verify_stream_valid() {
763 let rs = make_codec(3, 2);
764 let shard_size = 4096;
765
766 let d = vec![random_data(shard_size); 3];
767 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
768 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
769 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
770 .unwrap();
771
772 let mut all_data: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
774 for w in &writers {
775 all_data.push(w.as_slice());
776 }
777 let mut all_readers: Vec<&[u8]> = all_data;
778
779 assert!(
780 rs.verify_stream(&mut all_readers, &StreamOptions::default())
781 .unwrap()
782 );
783 }
784
785 #[test]
786 fn test_verify_stream_corrupted() {
787 let rs = make_codec(3, 2);
788 let shard_size = 4096;
789
790 let d = vec![random_data(shard_size); 3];
791 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
792 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
793 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
794 .unwrap();
795
796 writers[0][0] ^= 0xFF;
798
799 let mut all_data: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
800 for w in &writers {
801 all_data.push(w.as_slice());
802 }
803 let mut all_readers: Vec<&[u8]> = all_data;
804
805 assert!(
806 !rs.verify_stream(&mut all_readers, &StreamOptions::default())
807 .unwrap()
808 );
809 }
810
811 #[test]
812 fn test_reconstruct_stream_single_missing() {
813 let rs = make_codec(3, 2);
814 let shard_size = 4096;
815
816 let d: Vec<Vec<u8>> = (0..3).map(|_| random_data(shard_size)).collect();
817 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
818 let mut parity_writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
819 rs.encode_stream(&mut readers, &mut parity_writers, &StreamOptions::default())
820 .unwrap();
821
822 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
824 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(d[1].clone()),
826 std::io::Cursor::new(d[2].clone()),
827 std::io::Cursor::new(parity_writers[0].clone()),
828 std::io::Cursor::new(parity_writers[1].clone()),
829 ];
830
831 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
832 .unwrap();
833
834 assert_eq!(shards[0].get_ref(), &d[0]);
836 }
837
838 #[test]
839 fn test_reconstruct_non_streaming() {
840 let rs = make_codec(3, 2);
842 let shard_size = 4096;
843
844 let d: Vec<Vec<u8>> = (0..3).map(|_| random_data(shard_size)).collect();
845 let data_refs: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
846 let mut p0 = vec![0u8; shard_size];
847 let mut p1 = vec![0u8; shard_size];
848 let mut parity_refs: Vec<&mut [u8]> = vec![&mut p0, &mut p1];
849 rs.encode_sep(&data_refs, &mut parity_refs).unwrap();
850
851 let mut shards: Vec<Option<Vec<u8>>> = vec![
853 None,
854 Some(d[1].clone()),
855 Some(d[2].clone()),
856 Some(p0.clone()),
857 Some(p1.clone()),
858 ];
859 rs.reconstruct(&mut shards).unwrap();
860
861 assert_eq!(shards[0].as_ref().unwrap(), &d[0]);
862 }
863
864 #[test]
865 fn test_reconstruct_stream_basic() {
866 let rs = make_codec(3, 2);
867 let shard_size = 64;
868
869 let d: Vec<Vec<u8>> = (0..3).map(|_| random_data(shard_size)).collect();
870 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
871 let mut parity_writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
872 rs.encode_stream(&mut readers, &mut parity_writers, &StreamOptions::default())
873 .unwrap();
874
875 let all: Vec<&[u8]> = vec![
877 d[0].as_slice(),
878 d[1].as_slice(),
879 d[2].as_slice(),
880 parity_writers[0].as_slice(),
881 parity_writers[1].as_slice(),
882 ];
883 assert!(rs.verify(&all).unwrap());
884
885 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
887 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(d[1].clone()),
889 std::io::Cursor::new(d[2].clone()),
890 std::io::Cursor::new(parity_writers[0].clone()),
891 std::io::Cursor::new(parity_writers[1].clone()),
892 ];
893
894 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
895 .unwrap();
896
897 let recovered = shards[0].get_ref();
899 assert_eq!(
900 recovered.len(),
901 d[0].len(),
902 "recovered len {} != expected len {}",
903 recovered.len(),
904 d[0].len()
905 );
906 assert_eq!(
907 recovered,
908 &d[0],
909 "recovered: {:?}, expected: {:?}",
910 &recovered[..8],
911 &d[0][..8]
912 );
913 }
914
915 #[test]
916 fn test_stream_options_builder() {
917 let opts = StreamOptions::new().with_block_size(1024 * 1024);
918 assert_eq!(opts.block_size, 1024 * 1024);
919
920 let opts = StreamOptions::new().with_block_size(100);
922 assert_eq!(opts.block_size, 1024);
923 }
924
925 #[test]
926 fn test_stream_error_display() {
927 let e = StreamError::read(
928 3,
929 std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "eof"),
930 );
931 let s = format!("{e}");
932 assert!(s.contains("shard 3"));
933 assert!(s.contains("eof"));
934
935 let e = StreamError::codec(0, crate::Error::TooFewShardsPresent);
936 let s = format!("{e}");
937 assert!(s.contains("codec"));
938 }
939
940 #[test]
945 fn test_encode_stream_concurrent() {
946 let rs = make_codec(4, 2);
947 let shard_size = 8 * 1024;
948
949 let data: Vec<Vec<u8>> = (0..4).map(|_| random_data(shard_size)).collect();
950 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
951 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
952
953 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
954 .unwrap();
955
956 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
958 for w in &writers {
959 all.push(w.as_slice());
960 }
961 assert!(rs.verify(&all).unwrap());
962 }
963
964 #[test]
965 fn test_verify_stream_concurrent() {
966 let rs = make_codec(4, 2);
967 let shard_size = 8 * 1024;
968
969 let data: Vec<Vec<u8>> = (0..4).map(|_| random_data(shard_size)).collect();
970 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
971 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
972 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
973 .unwrap();
974
975 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
977 for w in &writers {
978 all.push(w.as_slice());
979 }
980 let mut all_readers: Vec<&[u8]> = all;
981 assert!(
982 rs.verify_stream(&mut all_readers, &StreamOptions::default())
983 .unwrap()
984 );
985
986 let mut corrupted = writers[0].clone();
988 corrupted[0] ^= 0xFF;
989 let mut all_corrupt: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
990 all_corrupt.push(corrupted.as_slice());
991 all_corrupt.push(writers[1].as_slice());
992 assert!(
993 !rs.verify_stream(&mut all_corrupt, &StreamOptions::default())
994 .unwrap()
995 );
996 }
997
998 #[test]
999 fn test_reconstruct_stream_concurrent() {
1000 let rs = make_codec(4, 2);
1001 let shard_size = 8 * 1024;
1002
1003 let data: Vec<Vec<u8>> = (0..4).map(|_| random_data(shard_size)).collect();
1004 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1005 let mut parity_writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
1006 rs.encode_stream(&mut readers, &mut parity_writers, &StreamOptions::default())
1007 .unwrap();
1008
1009 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
1011 std::io::Cursor::new(data[0].clone()),
1012 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(data[2].clone()),
1014 std::io::Cursor::new(data[3].clone()),
1015 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(parity_writers[1].clone()),
1017 ];
1018
1019 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
1020 .unwrap();
1021
1022 assert_eq!(shards[1].get_ref(), &data[1]);
1023 }
1024
1025 #[test]
1026 fn test_concurrent_stream_large_blocks() {
1027 let rs = make_codec(10, 4);
1028 let total_size = 1024 * 1024; let block_size = 256 * 1024; let data: Vec<Vec<u8>> = (0..10).map(|_| random_data(total_size)).collect();
1032 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1033 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 4];
1034
1035 let opts = StreamOptions::new().with_block_size(block_size);
1036 rs.encode_stream(&mut readers, &mut writers, &opts).unwrap();
1037
1038 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1040 for w in &writers {
1041 all.push(w.as_slice());
1042 }
1043 assert!(rs.verify(&all).unwrap());
1044
1045 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = Vec::new();
1047 for d in &data {
1048 shards.push(std::io::Cursor::new(d.clone()));
1049 }
1050 shards[0] = std::io::Cursor::new(Vec::new());
1051 shards[5] = std::io::Cursor::new(Vec::new());
1052 for w in &writers {
1053 shards.push(std::io::Cursor::new(w.clone()));
1054 }
1055
1056 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
1057 .unwrap();
1058
1059 assert_eq!(shards[0].get_ref(), &data[0]);
1060 assert_eq!(shards[5].get_ref(), &data[5]);
1061 }
1062
1063 #[test]
1064 fn test_encode_stream_zero_length() {
1065 let rs = ReedSolomon::new(3, 2).unwrap();
1066 let mut readers: Vec<&[u8]> = vec![b""; 3];
1067 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
1068 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
1070 .unwrap();
1071 assert!(writers.iter().all(|w| w.is_empty()));
1072 }
1073
1074 #[test]
1075 fn test_encode_stream_single_byte_block() {
1076 let rs = ReedSolomon::new(2, 1).unwrap();
1077 let d0 = vec![0xABu8; 4];
1078 let d1 = vec![0xCDu8; 4];
1079 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice()];
1080 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 1];
1081 let opts = StreamOptions::new().with_block_size(1);
1082 rs.encode_stream(&mut readers, &mut writers, &opts).unwrap();
1083
1084 let mut all: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice()];
1086 for w in &writers {
1087 all.push(w.as_slice());
1088 }
1089 assert!(rs.verify(&all).unwrap());
1090 }
1091
1092 #[test]
1093 fn test_reconstruct_stream_minimum_present() {
1094 let rs = ReedSolomon::new(3, 2).unwrap();
1095 let shard_len = 16usize;
1096 let data: Vec<Vec<u8>> = (0..3).map(|i| vec![i as u8 + 1; shard_len]).collect();
1097
1098 let refs: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1100 let mut parity = vec![vec![0u8; shard_len]; 2];
1101 let mut par_refs: Vec<&mut [u8]> = parity.iter_mut().map(|p| &mut p[..]).collect();
1102 rs.encode_sep(&refs, &mut par_refs).unwrap();
1103
1104 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
1108 std::io::Cursor::new(data[0].clone()),
1109 std::io::Cursor::new(Vec::new()),
1110 std::io::Cursor::new(Vec::new()),
1111 std::io::Cursor::new(parity[0].clone()),
1112 std::io::Cursor::new(parity[1].clone()),
1113 ];
1114
1115 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
1116 .unwrap();
1117
1118 assert_eq!(shards[1].get_ref(), &data[1], "shard 1 mismatch");
1120 assert_eq!(shards[2].get_ref(), &data[2], "shard 2 mismatch");
1121 }
1122}