1use std::io::Error;
31use std::mem;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum StreamIoMode {
40 Auto,
42 Serial,
44 Parallel,
46}
47
48#[derive(Debug, Clone)]
50pub struct StreamOptions {
51 pub block_size: usize,
56 pub io_mode: StreamIoMode,
58}
59
60impl Default for StreamOptions {
61 fn default() -> Self {
62 Self {
63 block_size: 4 * 1024 * 1024,
64 io_mode: StreamIoMode::Auto,
65 }
66 }
67}
68
69impl StreamOptions {
70 pub fn new() -> Self {
72 Self::default()
73 }
74
75 pub fn with_block_size(mut self, size: usize) -> Self {
77 const MIN_BLOCK_SIZE_BYTES: usize = 1024;
78 const MAX_BLOCK_SIZE_BYTES: usize = 16 * 1024 * 1024;
79 self.block_size = size.clamp(MIN_BLOCK_SIZE_BYTES, MAX_BLOCK_SIZE_BYTES);
80 self
81 }
82
83 pub fn with_io_mode(mut self, mode: StreamIoMode) -> Self {
85 self.io_mode = mode;
86 self
87 }
88}
89
90#[derive(Debug)]
96pub struct StreamError {
97 pub shard_index: usize,
99 pub kind: StreamErrorKind,
101}
102
103#[derive(Debug)]
105pub enum StreamErrorKind {
106 Read(std::io::Error),
108 Write(std::io::Error),
110 Codec(crate::Error),
112}
113
114impl core::fmt::Display for StreamError {
115 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
116 match &self.kind {
117 StreamErrorKind::Read(e) => {
118 write!(f, "read error on shard {}: {}", self.shard_index, e)
119 }
120 StreamErrorKind::Write(e) => {
121 write!(f, "write error on shard {}: {}", self.shard_index, e)
122 }
123 StreamErrorKind::Codec(e) => {
124 write!(f, "codec error on shard {}: {}", self.shard_index, e)
125 }
126 }
127 }
128}
129
130impl std::error::Error for StreamError {
131 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
132 match &self.kind {
133 StreamErrorKind::Read(e) => Some(e),
134 StreamErrorKind::Write(e) => Some(e),
135 StreamErrorKind::Codec(e) => Some(e),
136 }
137 }
138}
139
140impl StreamError {
141 fn read(shard_index: usize, e: std::io::Error) -> Self {
142 Self {
143 shard_index,
144 kind: StreamErrorKind::Read(e),
145 }
146 }
147
148 fn write(shard_index: usize, e: std::io::Error) -> Self {
149 Self {
150 shard_index,
151 kind: StreamErrorKind::Write(e),
152 }
153 }
154
155 fn codec(shard_index: usize, e: crate::Error) -> Self {
156 Self {
157 shard_index,
158 kind: StreamErrorKind::Codec(e),
159 }
160 }
161}
162
163fn take_stream_error(
164 first_error: &std::sync::Mutex<Option<StreamError>>,
165 fallback_message: &'static str,
166) -> StreamError {
167 match first_error.lock() {
168 Ok(mut guard) => guard
169 .take()
170 .unwrap_or_else(|| StreamError::read(0, Error::other(fallback_message))),
171 Err(poisoned) => {
172 let mut guard = poisoned.into_inner();
173 guard
174 .take()
175 .unwrap_or_else(|| StreamError::read(0, Error::other(fallback_message)))
176 }
177 }
178}
179
180fn use_parallel_stream_io(options: &StreamOptions, stream_count: usize) -> bool {
185 match options.io_mode {
186 StreamIoMode::Serial => false,
187 StreamIoMode::Parallel => true,
188 StreamIoMode::Auto => use_parallel_stream_io_auto(options.block_size, stream_count),
189 }
190}
191
192fn use_parallel_stream_io_auto(block_size: usize, stream_count: usize) -> bool {
193 if stream_count < 2 || block_size < 256 * 1024 {
194 return false;
195 }
196 if stream_count <= 6 && block_size <= 1024 * 1024 {
197 return false;
198 }
199
200 block_size >= 4 * 1024 * 1024 && stream_count >= 10
201}
202
203fn read_block_with_mode<R: std::io::Read + Send>(
204 readers: &mut [R],
205 buffers: &mut [Vec<u8>],
206 max_len: usize,
207 use_parallel_io: bool,
208 read_lengths: &mut Vec<(usize, usize)>,
209) -> Result<(bool, usize), StreamError> {
210 if use_parallel_io {
211 read_block_par(readers, buffers, max_len)
212 } else {
213 read_block(readers, buffers, max_len, read_lengths)
214 }
215}
216
217fn write_block_with_mode<W: std::io::Write + Send>(
218 writers: &mut [W],
219 buffers: &[Vec<u8>],
220 len: usize,
221 shard_offset: usize,
222 use_parallel_io: bool,
223) -> Result<(), StreamError> {
224 if use_parallel_io {
225 write_block_par(writers, buffers, len, shard_offset)
226 } else {
227 write_block(writers, buffers, len, shard_offset)
228 }
229}
230
231fn read_block<R: std::io::Read>(
237 readers: &mut [R],
238 buffers: &mut [Vec<u8>],
239 max_len: usize,
240 read_lengths: &mut Vec<(usize, usize)>,
241) -> Result<(bool, usize), StreamError> {
242 read_lengths.clear();
243
244 for (i, (reader, buf)) in readers.iter_mut().zip(buffers.iter_mut()).enumerate() {
245 let total = read_one_stream(reader, buf, max_len).map_err(|e| StreamError::read(i, e))?;
246 read_lengths.push((i, total));
247 }
248
249 let actual_len = read_lengths
250 .iter()
251 .map(|(_, total)| *total)
252 .max()
253 .unwrap_or(0);
254 zero_pad_short_buffers(buffers, read_lengths, actual_len);
255
256 Ok((actual_len == 0, actual_len))
257}
258
259fn prepare_read_buffer(buf: &mut Vec<u8>, max_len: usize) {
260 match buf.len().cmp(&max_len) {
261 core::cmp::Ordering::Less => buf.resize(max_len, 0),
262 core::cmp::Ordering::Greater => buf.truncate(max_len),
263 core::cmp::Ordering::Equal => {}
264 }
265}
266
267fn read_one_stream<R: std::io::Read>(
268 reader: &mut R,
269 buf: &mut Vec<u8>,
270 max_len: usize,
271) -> Result<usize, std::io::Error> {
272 prepare_read_buffer(buf, max_len);
273 let mut total = 0;
274
275 while total < max_len {
276 match reader.read(&mut buf[total..]) {
277 Ok(0) => break,
278 Ok(n) => total += n,
279 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
280 Err(e) => return Err(e),
281 }
282 }
283
284 Ok(total)
285}
286
287fn zero_pad_short_buffers(
288 buffers: &mut [Vec<u8>],
289 read_lengths: &[(usize, usize)],
290 actual_len: usize,
291) {
292 for &(i, total) in read_lengths {
293 if total < actual_len {
294 buffers[i][total..actual_len].fill(0);
295 }
296 }
297}
298
299fn read_present_cursors_with_mode(
300 shards: &mut [std::io::Cursor<Vec<u8>>],
301 buffers: &mut [Vec<u8>],
302 present: &[bool],
303 present_indices: &[usize],
304 max_len: usize,
305 use_parallel_io: bool,
306 read_lengths: &mut Vec<(usize, usize)>,
307) -> Result<(bool, usize), StreamError> {
308 read_lengths.clear();
309
310 if use_parallel_io {
311 use rayon::prelude::*;
312
313 *read_lengths = shards
314 .par_iter_mut()
315 .zip(buffers.par_iter_mut())
316 .enumerate()
317 .filter_map(|(i, item)| present[i].then_some((i, item)))
318 .map(|(i, (shard, buf))| {
319 let total =
320 read_one_stream(shard, buf, max_len).map_err(|e| StreamError::read(i, e))?;
321 buf.truncate(total);
322 Ok::<_, StreamError>((i, total))
323 })
324 .collect::<Result<Vec<_>, _>>()?;
325 } else {
326 for &i in present_indices {
327 let total = read_one_stream(&mut shards[i], &mut buffers[i], max_len)
328 .map_err(|e| StreamError::read(i, e))?;
329 buffers[i].truncate(total);
330 read_lengths.push((i, total));
331 }
332 }
333
334 let actual_len = read_lengths
335 .iter()
336 .map(|(_, total)| *total)
337 .max()
338 .unwrap_or(0);
339 if actual_len != 0 {
340 for &idx in present_indices {
341 buffers[idx].resize(actual_len, 0);
342 }
343 }
344
345 Ok((actual_len == 0, actual_len))
346}
347
348fn write_block<W: std::io::Write>(
350 writers: &mut [W],
351 buffers: &[Vec<u8>],
352 len: usize,
353 shard_offset: usize,
354) -> Result<(), StreamError> {
355 for (i, (writer, buf)) in writers.iter_mut().zip(buffers.iter()).enumerate() {
356 let mut written = 0;
357 while written < len {
358 match writer.write(&buf[written..len]) {
359 Ok(0) => {
360 return Err(StreamError::write(
361 shard_offset + i,
362 std::io::Error::new(std::io::ErrorKind::WriteZero, "write returned 0"),
363 ));
364 }
365 Ok(n) => written += n,
366 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
367 Err(e) => return Err(StreamError::write(shard_offset + i, e)),
368 }
369 }
370 }
371 Ok(())
372}
373
374fn read_block_par<R: std::io::Read + Send>(
380 readers: &mut [R],
381 buffers: &mut [Vec<u8>],
382 max_len: usize,
383) -> Result<(bool, usize), StreamError> {
384 use rayon::prelude::*;
385
386 let read_lengths: Vec<(usize, usize)> = readers
387 .par_iter_mut()
388 .zip(buffers.par_iter_mut())
389 .enumerate()
390 .map(|(i, (reader, buf))| {
391 read_one_stream(reader, buf, max_len)
392 .map(|total| (i, total))
393 .map_err(|e| StreamError::read(i, e))
394 })
395 .collect::<Result<Vec<_>, _>>()?;
396
397 let actual_len = read_lengths
398 .iter()
399 .map(|(_, total)| *total)
400 .max()
401 .unwrap_or(0);
402 zero_pad_short_buffers(buffers, &read_lengths, actual_len);
403
404 Ok((actual_len == 0, actual_len))
405}
406
407fn write_block_par<W: std::io::Write + Send>(
409 writers: &mut [W],
410 buffers: &[Vec<u8>],
411 len: usize,
412 shard_offset: usize,
413) -> Result<(), StreamError> {
414 use rayon::prelude::*;
415
416 let first_error: std::sync::Mutex<Option<StreamError>> = std::sync::Mutex::new(None);
417
418 writers
419 .par_iter_mut()
420 .zip(buffers.par_iter())
421 .enumerate()
422 .try_for_each(|(i, (writer, buf))| {
423 let mut written = 0;
424 while written < len {
425 match writer.write(&buf[written..len]) {
426 Ok(0) => {
427 if let Ok(mut guard) = first_error.lock()
428 && guard.is_none()
429 {
430 *guard = Some(StreamError::write(
431 shard_offset + i,
432 std::io::Error::new(
433 std::io::ErrorKind::WriteZero,
434 "write returned 0",
435 ),
436 ));
437 }
438 return Err(());
439 }
440 Ok(n) => written += n,
441 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
442 Err(e) => {
443 if let Ok(mut guard) = first_error.lock()
444 && guard.is_none()
445 {
446 *guard = Some(StreamError::write(shard_offset + i, e));
447 }
448 return Err(());
449 }
450 }
451 }
452 Ok(())
453 })
454 .map_err(|()| {
455 take_stream_error(
456 &first_error,
457 "parallel stream writer error was not reported",
458 )
459 })
460}
461
462impl super::ReedSolomon<crate::galois_8::Field> {
467 pub fn encode_stream(
477 &self,
478 data: &mut [impl std::io::Read + Send],
479 parity: &mut [impl std::io::Write + Send],
480 options: &StreamOptions,
481 ) -> Result<(), StreamError> {
482 let block_size = options.block_size;
483 let data_count = self.data_shard_count;
484 let parity_count = self.parity_shard_count;
485 let use_parallel_read = use_parallel_stream_io(options, data_count);
486 let use_parallel_write = use_parallel_stream_io(options, parity_count);
487
488 debug_assert_eq!(data.len(), data_count);
489 debug_assert_eq!(parity.len(), parity_count);
490
491 let mut data_bufs: Vec<Vec<u8>> = (0..data_count)
492 .map(|_| Vec::with_capacity(block_size))
493 .collect();
494 let mut parity_bufs: Vec<Vec<u8>> = (0..parity_count)
495 .map(|_| Vec::with_capacity(block_size))
496 .collect();
497 let mut read_lengths = Vec::with_capacity(data_count);
498
499 loop {
500 let (all_eof, actual_len) = read_block_with_mode(
501 data,
502 &mut data_bufs,
503 block_size,
504 use_parallel_read,
505 &mut read_lengths,
506 )?;
507 if all_eof {
508 break;
509 }
510
511 for buf in parity_bufs.iter_mut() {
513 buf.resize(actual_len, 0);
514 }
515
516 let data_refs: Vec<&[u8]> = data_bufs.iter().map(|b| &b[..actual_len]).collect();
518 let mut parity_refs: Vec<&mut [u8]> = parity_bufs
519 .iter_mut()
520 .map(|b| &mut b[..actual_len])
521 .collect();
522
523 self.encode_sep_par(&data_refs, &mut parity_refs)
524 .map_err(|e| StreamError::codec(0, e))?;
525
526 write_block_with_mode(
528 parity,
529 &parity_bufs,
530 actual_len,
531 data_count,
532 use_parallel_write,
533 )?;
534 }
535
536 Ok(())
537 }
538
539 pub fn verify_stream(
545 &self,
546 shards: &mut [impl std::io::Read + Send],
547 options: &StreamOptions,
548 ) -> Result<bool, StreamError> {
549 let block_size = options.block_size;
550 let total = self.total_shard_count;
551 let use_parallel_read = use_parallel_stream_io(options, total);
552
553 debug_assert_eq!(shards.len(), total);
554
555 let mut bufs: Vec<Vec<u8>> = (0..total).map(|_| Vec::with_capacity(block_size)).collect();
556 let mut read_lengths = Vec::with_capacity(total);
557
558 loop {
559 let (all_eof, actual_len) = read_block_with_mode(
560 shards,
561 &mut bufs,
562 block_size,
563 use_parallel_read,
564 &mut read_lengths,
565 )?;
566 if all_eof {
567 break;
568 }
569
570 let refs: Vec<&[u8]> = bufs.iter().map(|b| &b[..actual_len]).collect();
571
572 let valid = self
573 .verify_par(&refs)
574 .map_err(|e| StreamError::codec(0, e))?;
575 if !valid {
576 return Ok(false);
577 }
578 }
579
580 Ok(true)
581 }
582
583 pub fn reconstruct_stream(
599 &self,
600 shards: &mut [std::io::Cursor<Vec<u8>>],
601 options: &StreamOptions,
602 ) -> Result<(), StreamError> {
603 let block_size = options.block_size;
604 let total = self.total_shard_count;
605
606 debug_assert_eq!(shards.len(), total);
607
608 let mut present = vec![false; total];
610 for (i, shard) in shards.iter().enumerate() {
611 present[i] = !shard.get_ref().is_empty();
612 }
613
614 let missing_count = present.iter().filter(|&&p| !p).count();
615 if missing_count > self.parity_shard_count {
616 return Err(StreamError::codec(0, crate::Error::TooFewShardsPresent));
617 }
618 let present_count = total - missing_count;
619 let use_parallel_read = use_parallel_stream_io(options, present_count);
620 let present_indices: Vec<usize> = present
621 .iter()
622 .enumerate()
623 .filter_map(|(i, is_present)| is_present.then_some(i))
624 .collect();
625 let missing_indices: Vec<usize> = present
626 .iter()
627 .enumerate()
628 .filter_map(|(i, is_present)| (!is_present).then_some(i))
629 .collect();
630
631 let mut bufs: Vec<Vec<u8>> = (0..total)
638 .map(|i| {
639 if present[i] {
640 Vec::with_capacity(block_size)
641 } else {
642 Vec::new()
643 }
644 })
645 .collect();
646 let mut reconstruct_bufs: Vec<Option<Vec<u8>>> = (0..total).map(|_| None).collect();
647 let mut read_lengths = Vec::with_capacity(total);
648
649 loop {
650 let (all_eof, actual_len) = read_present_cursors_with_mode(
651 shards,
652 &mut bufs,
653 &present,
654 &present_indices,
655 block_size,
656 use_parallel_read,
657 &mut read_lengths,
658 )?;
659 if all_eof {
660 break;
661 }
662
663 for &idx in &present_indices {
666 bufs[idx].resize(actual_len, 0);
667 reconstruct_bufs[idx] = Some(mem::take(&mut bufs[idx]));
668 }
669 for &idx in &missing_indices {
670 reconstruct_bufs[idx] = None;
671 }
672
673 self.reconstruct(&mut reconstruct_bufs)
674 .map_err(|e| StreamError::codec(0, e))?;
675
676 for &idx in &missing_indices {
679 let recovered = reconstruct_bufs[idx]
680 .as_ref()
681 .expect("missing shard buffer");
682 shards[idx]
683 .get_mut()
684 .extend_from_slice(&recovered[..actual_len]);
685 }
686
687 for idx in 0..total {
688 if let Some(buf) = reconstruct_bufs[idx].take() {
689 bufs[idx] = buf;
690 }
691 }
692 }
693
694 Ok(())
695 }
696}
697
698#[cfg(test)]
699mod tests {
700 use super::*;
701 use crate::galois_8::ReedSolomon;
702
703 fn make_codec(data: usize, parity: usize) -> ReedSolomon {
704 ReedSolomon::new(data, parity).unwrap()
705 }
706
707 fn random_data(len: usize) -> Vec<u8> {
708 (0..len)
710 .map(|i| (i.wrapping_mul(73).wrapping_add(17)) as u8)
711 .collect()
712 }
713
714 #[test]
715 fn test_encode_stream_basic() {
716 let rs = make_codec(3, 2);
717 let shard_size = 4096;
718
719 let d0 = random_data(shard_size);
720 let d1 = random_data(shard_size);
721 let d2 = random_data(shard_size);
722
723 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice(), d2.as_slice()];
724 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
725
726 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
727 .unwrap();
728
729 assert_eq!(writers[0].len(), shard_size);
730 assert_eq!(writers[1].len(), shard_size);
731
732 let all: Vec<&[u8]> = vec![
734 d0.as_slice(),
735 d1.as_slice(),
736 d2.as_slice(),
737 writers[0].as_slice(),
738 writers[1].as_slice(),
739 ];
740 assert!(rs.verify(&all).unwrap());
741 }
742
743 #[test]
744 fn test_encode_stream_multi_block() {
745 let rs = make_codec(3, 2);
746 let total_size = 10 * 1024; let block_size = 4096;
748
749 let d0 = random_data(total_size);
750 let d1 = random_data(total_size);
751 let d2 = random_data(total_size);
752
753 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice(), d2.as_slice()];
754 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
755
756 let opts = StreamOptions::new().with_block_size(block_size);
757 rs.encode_stream(&mut readers, &mut writers, &opts).unwrap();
758
759 assert_eq!(writers[0].len(), total_size);
760 assert_eq!(writers[1].len(), total_size);
761 }
762
763 #[test]
764 fn test_encode_stream_empty() {
765 let rs = make_codec(3, 2);
766 let empty: Vec<&[u8]> = vec![&[], &[], &[]];
767 let mut readers = empty.clone();
768 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
769
770 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
771 .unwrap();
772
773 assert!(writers[0].is_empty());
775 assert!(writers[1].is_empty());
776 }
777
778 #[test]
779 fn test_encode_stream_unequal_lengths() {
780 let rs = make_codec(3, 2);
781
782 let d0 = random_data(1000);
783 let d1 = random_data(500);
784 let d2 = random_data(800);
785
786 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice(), d2.as_slice()];
787 let mut writers: Vec<Vec<u8>> = vec![Vec::new(), Vec::new()];
788
789 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
790 .unwrap();
791
792 assert_eq!(writers[0].len(), 1000);
795 assert_eq!(writers[1].len(), 1000);
796 }
797
798 #[test]
799 fn test_encode_stream_10x4() {
800 let rs = make_codec(10, 4);
801 let shard_size = 64 * 1024;
802
803 let data: Vec<Vec<u8>> = (0..10).map(|_| random_data(shard_size)).collect();
804 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
805 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 4];
806
807 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
808 .unwrap();
809
810 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
812 for w in &writers {
813 all.push(w.as_slice());
814 }
815 assert!(rs.verify(&all).unwrap());
816 }
817
818 #[test]
819 fn test_verify_stream_valid() {
820 let rs = make_codec(3, 2);
821 let shard_size = 4096;
822
823 let d = vec![random_data(shard_size); 3];
824 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
825 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
826 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
827 .unwrap();
828
829 let mut all_data: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
831 for w in &writers {
832 all_data.push(w.as_slice());
833 }
834 let mut all_readers: Vec<&[u8]> = all_data;
835
836 assert!(
837 rs.verify_stream(&mut all_readers, &StreamOptions::default())
838 .unwrap()
839 );
840 }
841
842 #[test]
843 fn test_verify_stream_corrupted() {
844 let rs = make_codec(3, 2);
845 let shard_size = 4096;
846
847 let d = vec![random_data(shard_size); 3];
848 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
849 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
850 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
851 .unwrap();
852
853 writers[0][0] ^= 0xFF;
855
856 let mut all_data: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
857 for w in &writers {
858 all_data.push(w.as_slice());
859 }
860 let mut all_readers: Vec<&[u8]> = all_data;
861
862 assert!(
863 !rs.verify_stream(&mut all_readers, &StreamOptions::default())
864 .unwrap()
865 );
866 }
867
868 #[test]
869 fn test_reconstruct_stream_single_missing() {
870 let rs = make_codec(3, 2);
871 let shard_size = 4096;
872
873 let d: Vec<Vec<u8>> = (0..3).map(|_| random_data(shard_size)).collect();
874 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
875 let mut parity_writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
876 rs.encode_stream(&mut readers, &mut parity_writers, &StreamOptions::default())
877 .unwrap();
878
879 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
881 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(d[1].clone()),
883 std::io::Cursor::new(d[2].clone()),
884 std::io::Cursor::new(parity_writers[0].clone()),
885 std::io::Cursor::new(parity_writers[1].clone()),
886 ];
887
888 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
889 .unwrap();
890
891 assert_eq!(shards[0].get_ref(), &d[0]);
893 }
894
895 #[test]
896 fn test_reconstruct_non_streaming() {
897 let rs = make_codec(3, 2);
899 let shard_size = 4096;
900
901 let d: Vec<Vec<u8>> = (0..3).map(|_| random_data(shard_size)).collect();
902 let data_refs: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
903 let mut p0 = vec![0u8; shard_size];
904 let mut p1 = vec![0u8; shard_size];
905 let mut parity_refs: Vec<&mut [u8]> = vec![&mut p0, &mut p1];
906 rs.encode_sep(&data_refs, &mut parity_refs).unwrap();
907
908 let mut shards: Vec<Option<Vec<u8>>> = vec![
910 None,
911 Some(d[1].clone()),
912 Some(d[2].clone()),
913 Some(p0.clone()),
914 Some(p1.clone()),
915 ];
916 rs.reconstruct(&mut shards).unwrap();
917
918 assert_eq!(shards[0].as_ref().unwrap(), &d[0]);
919 }
920
921 #[test]
922 fn test_reconstruct_stream_basic() {
923 let rs = make_codec(3, 2);
924 let shard_size = 64;
925
926 let d: Vec<Vec<u8>> = (0..3).map(|_| random_data(shard_size)).collect();
927 let mut readers: Vec<&[u8]> = d.iter().map(|s| s.as_slice()).collect();
928 let mut parity_writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
929 rs.encode_stream(&mut readers, &mut parity_writers, &StreamOptions::default())
930 .unwrap();
931
932 let all: Vec<&[u8]> = vec![
934 d[0].as_slice(),
935 d[1].as_slice(),
936 d[2].as_slice(),
937 parity_writers[0].as_slice(),
938 parity_writers[1].as_slice(),
939 ];
940 assert!(rs.verify(&all).unwrap());
941
942 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
944 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(d[1].clone()),
946 std::io::Cursor::new(d[2].clone()),
947 std::io::Cursor::new(parity_writers[0].clone()),
948 std::io::Cursor::new(parity_writers[1].clone()),
949 ];
950
951 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
952 .unwrap();
953
954 let recovered = shards[0].get_ref();
956 assert_eq!(
957 recovered.len(),
958 d[0].len(),
959 "recovered len {} != expected len {}",
960 recovered.len(),
961 d[0].len()
962 );
963 assert_eq!(
964 recovered,
965 &d[0],
966 "recovered: {:?}, expected: {:?}",
967 &recovered[..8],
968 &d[0][..8]
969 );
970 }
971
972 #[test]
973 fn test_stream_options_builder() {
974 let opts = StreamOptions::new().with_block_size(1024 * 1024);
975 assert_eq!(opts.block_size, 1024 * 1024);
976 assert_eq!(opts.io_mode, StreamIoMode::Auto);
977
978 let opts = StreamOptions::new().with_block_size(100);
980 assert_eq!(opts.block_size, 1024);
981
982 let opts = StreamOptions::new().with_io_mode(StreamIoMode::Serial);
983 assert_eq!(opts.io_mode, StreamIoMode::Serial);
984 }
985
986 #[test]
987 fn test_stream_io_auto_decision_thresholds() {
988 let opts = StreamOptions::new()
989 .with_block_size(64 * 1024)
990 .with_io_mode(StreamIoMode::Auto);
991 assert!(!use_parallel_stream_io(&opts, 14));
992
993 let opts = StreamOptions::new()
994 .with_block_size(1024 * 1024)
995 .with_io_mode(StreamIoMode::Auto);
996 assert!(!use_parallel_stream_io(&opts, 6));
997
998 let opts = StreamOptions::new()
999 .with_block_size(4 * 1024 * 1024)
1000 .with_io_mode(StreamIoMode::Auto);
1001 assert!(use_parallel_stream_io(&opts, 10));
1002
1003 let opts = StreamOptions::new()
1004 .with_block_size(64 * 1024)
1005 .with_io_mode(StreamIoMode::Parallel);
1006 assert!(use_parallel_stream_io(&opts, 1));
1007 }
1008
1009 #[test]
1010 fn test_stream_error_display() {
1011 let e = StreamError::read(
1012 3,
1013 std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "eof"),
1014 );
1015 let s = format!("{e}");
1016 assert!(s.contains("shard 3"));
1017 assert!(s.contains("eof"));
1018
1019 let e = StreamError::codec(0, crate::Error::TooFewShardsPresent);
1020 let s = format!("{e}");
1021 assert!(s.contains("codec"));
1022 }
1023
1024 #[test]
1029 fn test_encode_stream_concurrent() {
1030 let rs = make_codec(4, 2);
1031 let shard_size = 8 * 1024;
1032
1033 let data: Vec<Vec<u8>> = (0..4).map(|_| random_data(shard_size)).collect();
1034 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1035 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
1036
1037 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
1038 .unwrap();
1039
1040 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1042 for w in &writers {
1043 all.push(w.as_slice());
1044 }
1045 assert!(rs.verify(&all).unwrap());
1046 }
1047
1048 #[test]
1049 fn test_verify_stream_concurrent() {
1050 let rs = make_codec(4, 2);
1051 let shard_size = 8 * 1024;
1052
1053 let data: Vec<Vec<u8>> = (0..4).map(|_| random_data(shard_size)).collect();
1054 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1055 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
1056 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
1057 .unwrap();
1058
1059 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1061 for w in &writers {
1062 all.push(w.as_slice());
1063 }
1064 let mut all_readers: Vec<&[u8]> = all;
1065 assert!(
1066 rs.verify_stream(&mut all_readers, &StreamOptions::default())
1067 .unwrap()
1068 );
1069
1070 let mut corrupted = writers[0].clone();
1072 corrupted[0] ^= 0xFF;
1073 let mut all_corrupt: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1074 all_corrupt.push(corrupted.as_slice());
1075 all_corrupt.push(writers[1].as_slice());
1076 assert!(
1077 !rs.verify_stream(&mut all_corrupt, &StreamOptions::default())
1078 .unwrap()
1079 );
1080 }
1081
1082 #[test]
1083 fn test_reconstruct_stream_concurrent() {
1084 let rs = make_codec(4, 2);
1085 let shard_size = 8 * 1024;
1086
1087 let data: Vec<Vec<u8>> = (0..4).map(|_| random_data(shard_size)).collect();
1088 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1089 let mut parity_writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
1090 rs.encode_stream(&mut readers, &mut parity_writers, &StreamOptions::default())
1091 .unwrap();
1092
1093 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
1095 std::io::Cursor::new(data[0].clone()),
1096 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(data[2].clone()),
1098 std::io::Cursor::new(data[3].clone()),
1099 std::io::Cursor::new(Vec::new()), std::io::Cursor::new(parity_writers[1].clone()),
1101 ];
1102
1103 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
1104 .unwrap();
1105
1106 assert_eq!(shards[1].get_ref(), &data[1]);
1107 }
1108
1109 #[test]
1110 fn test_concurrent_stream_large_blocks() {
1111 let rs = make_codec(10, 4);
1112 let total_size = 1024 * 1024; let block_size = 256 * 1024; let data: Vec<Vec<u8>> = (0..10).map(|_| random_data(total_size)).collect();
1116 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1117 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 4];
1118
1119 let opts = StreamOptions::new().with_block_size(block_size);
1120 rs.encode_stream(&mut readers, &mut writers, &opts).unwrap();
1121
1122 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1124 for w in &writers {
1125 all.push(w.as_slice());
1126 }
1127 assert!(rs.verify(&all).unwrap());
1128
1129 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = Vec::new();
1131 for d in &data {
1132 shards.push(std::io::Cursor::new(d.clone()));
1133 }
1134 shards[0] = std::io::Cursor::new(Vec::new());
1135 shards[5] = std::io::Cursor::new(Vec::new());
1136 for w in &writers {
1137 shards.push(std::io::Cursor::new(w.clone()));
1138 }
1139
1140 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
1141 .unwrap();
1142
1143 assert_eq!(shards[0].get_ref(), &data[0]);
1144 assert_eq!(shards[5].get_ref(), &data[5]);
1145 }
1146
1147 #[test]
1148 fn test_encode_stream_zero_length() {
1149 let rs = ReedSolomon::new(3, 2).unwrap();
1150 let mut readers: Vec<&[u8]> = vec![b""; 3];
1151 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
1152 rs.encode_stream(&mut readers, &mut writers, &StreamOptions::default())
1154 .unwrap();
1155 assert!(writers.iter().all(|w| w.is_empty()));
1156 }
1157
1158 #[test]
1159 fn test_encode_stream_single_byte_block() {
1160 let rs = ReedSolomon::new(2, 1).unwrap();
1161 let d0 = vec![0xABu8; 4];
1162 let d1 = vec![0xCDu8; 4];
1163 let mut readers: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice()];
1164 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 1];
1165 let opts = StreamOptions::new().with_block_size(1);
1166 rs.encode_stream(&mut readers, &mut writers, &opts).unwrap();
1167
1168 let mut all: Vec<&[u8]> = vec![d0.as_slice(), d1.as_slice()];
1170 for w in &writers {
1171 all.push(w.as_slice());
1172 }
1173 assert!(rs.verify(&all).unwrap());
1174 }
1175
1176 #[test]
1177 fn test_stream_io_modes_encode_verify_match() {
1178 let rs = ReedSolomon::new(4, 2).unwrap();
1179 let shard_size = 32 * 1024;
1180 let data: Vec<Vec<u8>> = (0..4).map(|_| random_data(shard_size)).collect();
1181
1182 let mut expected_parity = Vec::new();
1183 for mode in [
1184 StreamIoMode::Auto,
1185 StreamIoMode::Serial,
1186 StreamIoMode::Parallel,
1187 ] {
1188 let opts = StreamOptions::new()
1189 .with_block_size(8 * 1024)
1190 .with_io_mode(mode);
1191 let mut readers: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1192 let mut writers: Vec<Vec<u8>> = vec![Vec::new(); 2];
1193
1194 rs.encode_stream(&mut readers, &mut writers, &opts).unwrap();
1195 if expected_parity.is_empty() {
1196 expected_parity = writers.clone();
1197 } else {
1198 assert_eq!(writers, expected_parity);
1199 }
1200
1201 let mut all: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1202 for parity in &writers {
1203 all.push(parity.as_slice());
1204 }
1205 let mut verify_readers = all;
1206 assert!(rs.verify_stream(&mut verify_readers, &opts).unwrap());
1207 }
1208 }
1209
1210 #[test]
1211 fn test_reconstruct_stream_minimum_present() {
1212 let rs = ReedSolomon::new(3, 2).unwrap();
1213 let shard_len = 16usize;
1214 let data: Vec<Vec<u8>> = (0..3).map(|i| vec![i as u8 + 1; shard_len]).collect();
1215
1216 let refs: Vec<&[u8]> = data.iter().map(|d| d.as_slice()).collect();
1218 let mut parity = vec![vec![0u8; shard_len]; 2];
1219 let mut par_refs: Vec<&mut [u8]> = parity.iter_mut().map(|p| &mut p[..]).collect();
1220 rs.encode_sep(&refs, &mut par_refs).unwrap();
1221
1222 let mut shards: Vec<std::io::Cursor<Vec<u8>>> = vec![
1226 std::io::Cursor::new(data[0].clone()),
1227 std::io::Cursor::new(Vec::new()),
1228 std::io::Cursor::new(Vec::new()),
1229 std::io::Cursor::new(parity[0].clone()),
1230 std::io::Cursor::new(parity[1].clone()),
1231 ];
1232
1233 rs.reconstruct_stream(&mut shards, &StreamOptions::default())
1234 .unwrap();
1235
1236 assert_eq!(shards[1].get_ref(), &data[1], "shard 1 mismatch");
1238 assert_eq!(shards[2].get_ref(), &data[2], "shard 2 mismatch");
1239 }
1240}