use anyhow::Result; use log::trace; use std::{ io::{Read, Seek, SeekFrom}, sync::{Arc, Mutex}, }; /// Splits an reader into many by seeking in between reads. pub struct MultiReader { position: u64, inner: Arc>, } impl MultiReader { pub fn new(mut inner: T) -> Result { let position = inner.stream_position()?; Ok(Self { position, inner: Arc::new(Mutex::new((position, inner))), }) } } impl Clone for MultiReader { fn clone(&self) -> Self { Self { position: self.position, inner: self.inner.clone(), } } } impl Read for MultiReader { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { let mut g = self.inner.lock().unwrap(); if g.0 != self.position { trace!( "seeking to match stream position ({} actual vs {} required)", g.0, self.position ); g.1.seek(SeekFrom::Start(self.position))?; g.0 = self.position; } let size = g.1.read(buf)?; g.0 += size as u64; self.position += size as u64; Ok(size) } } impl Seek for MultiReader { fn seek(&mut self, pos: SeekFrom) -> std::io::Result { self.position = match pos { SeekFrom::Start(x) => x, SeekFrom::Current(x) => self.position.saturating_add_signed(x), SeekFrom::End(_) => unimplemented!(), }; Ok(self.position) } }