use std::io::prelude::*; use std::io; use std::cmp; pub trait ReadSeek: Read + Seek {} impl ReadSeek for T where T: Read + Seek {} pub trait ReadWriteSeek: Read + Write + Seek {} impl ReadWriteSeek for T where T: Read + Write + Seek {} const BUF_SIZE: usize = 512; pub struct BufStream { inner: T, buf: [u8; BUF_SIZE], buf_offset: usize, buf_len: usize, dirty: bool, inner_offset: usize, } impl BufStream { pub fn new(inner: T) -> Self { BufStream:: { inner, buf: [0; BUF_SIZE], buf_offset: 0, buf_len: 0, dirty: false, inner_offset: 0, } } } impl Read for BufStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut num_done = 0; let mut num_todo = buf.len(); let mut eof = false; loop { let num_ready = cmp::min(num_todo, self.buf_len - self.buf_offset); buf[num_done..num_done+num_ready].clone_from_slice(&self.buf[self.buf_offset..self.buf_offset+num_ready]); self.buf_offset += num_ready; num_done += num_ready; num_todo -= num_ready; if eof || num_todo == 0 { break; } if num_todo > BUF_SIZE { let num_read = self.inner.read(&mut buf[num_done..])?; num_done += num_read; num_todo -= num_read; let num_copy = cmp::min(BUF_SIZE, num_done); self.buf[..num_copy].clone_from_slice(&buf[num_done - num_copy..]); self.buf_len = num_copy; self.buf_offset = num_copy; self.inner_offset = num_copy; eof = true; } else { if self.inner_offset != self.buf_offset { self.inner.seek(io::SeekFrom::Current((self.buf_offset - self.inner_offset) as i64))?; } self.buf_len = self.inner.read(&mut self.buf)?; self.buf_offset = 0; self.inner_offset = self.buf_len; eof = true; } } Ok(num_done) } } impl BufStream { fn write_buf(&mut self) -> io::Result<()> { if self.dirty { if self.inner_offset > 0 { self.inner.seek(io::SeekFrom::Current(-(self.inner_offset as i64)))?; } self.inner.write(&self.buf[..self.buf_len])?; self.inner_offset = self.buf_len; self.dirty = false; } Ok(()) } } impl Write for BufStream { fn write(&mut self, buf: &[u8]) -> io::Result { let mut num_done = 0; let mut num_todo = buf.len(); loop { let num_ready = cmp::min(num_todo, BUF_SIZE - self.buf_offset); self.buf[self.buf_offset..self.buf_offset+num_ready].clone_from_slice(&buf[num_done..num_done+num_ready]); self.buf_offset += num_ready; self.buf_len = cmp::max(self.buf_len, self.buf_offset); self.dirty = num_ready > 0; num_done += num_ready; num_todo -= num_ready; if num_todo == 0 { break; } self.write_buf()?; self.buf_offset = 0; self.buf_len = 0; self.inner_offset = 0; } Ok(num_done) } fn flush(&mut self) -> io::Result<()> { self.write_buf()?; self.inner.flush() } } impl Seek for BufStream { fn seek(&mut self, pos: io::SeekFrom) -> io::Result { // FIXME: reuse buffer let new_pos = match pos { io::SeekFrom::Current(x) => io::SeekFrom::Current(x - self.inner_offset as i64 + self.buf_offset as i64), _ => pos, }; self.buf_offset = 0; self.buf_len = 0; self.inner_offset = 0; self.inner.seek(new_pos) } } impl Drop for BufStream { fn drop(&mut self) { self.flush().expect("flush failed!"); } }