diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 8ab0d3a550401..b3c73d395a458 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -113,6 +113,17 @@ impl Reader for BufferedReader { self.pos += nread; Ok(nread) } + + fn skip(&mut self, num_bytes: uint) -> IoResult { + let bytes_in_buf = self.cap - self.pos; + Ok( if num_bytes <= bytes_in_buf { + self.pos += num_bytes; + num_bytes + } else { + self.pos += bytes_in_buf; + bytes_in_buf + try!( self.inner.skip(num_bytes - bytes_in_buf) ) + } ) + } } /// Wraps a Writer and buffers output to it diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index 8632fc63e52f8..b5b8860422ce3 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -259,6 +259,12 @@ impl File { err.update_err("couldn't fstat file", |e| format!("{}; path={}", e, self.path.display())) } + + /// Call Seek::skip(), not Reader::skip(). + #[inline] + pub fn skip(&mut self, num_bytes: uint) -> IoResult { + Seek::skip(self, num_bytes) + } } /// Unlink a file from the underlying filesystem. diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index f86ae05d623ca..0ecbea269e773 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -137,6 +137,12 @@ impl MemReader { /// Unwraps this `MemReader`, returning the underlying buffer #[inline] pub fn unwrap(self) -> Vec { self.buf } + + /// Call Seek::skip(), not Reader::skip(). + #[inline] + pub fn skip(&mut self, num_bytes: uint) -> IoResult { + Seek::skip(self, num_bytes) + } } impl Reader for MemReader { diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 8592d48974a25..d37cf46107aa9 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -228,20 +228,22 @@ use fmt; use int; use iter::Iterator; use libc; -use mem::transmute; +use mem::{transmute, uninitialized}; // std::mem is shadowed by self::mem +use cmp; use ops::{BitOr, BitXor, BitAnd, Sub, Not}; use option::{Option, Some, None}; use os; use boxed::Box; use result::{Ok, Err, Result}; use rt::rtio; -use slice::{AsSlice, ImmutableSlice}; +use slice::{AsSlice, ImmutableSlice, MutableSlice}; use str::{Str, StrSlice}; use str; use string::String; use uint; use unicode::char::UnicodeChar; use vec::Vec; +use num::ToPrimitive; // Reexports pub use self::stdio::stdin; @@ -521,7 +523,8 @@ impl UpdateIoError for IoResult { } } -static NO_PROGRESS_LIMIT: uint = 1000; +const NO_PROGRESS_LIMIT: uint = 1000; +const SKIP_SIZE: uint = 1 << 10; /// A trait for objects which are byte-oriented streams. Readers are defined by /// one method, `read`. This function will block until data is available, @@ -940,6 +943,23 @@ pub trait Reader { fn by_ref<'a>(&'a mut self) -> RefReader<'a, Self> { RefReader { inner: self } } + + /// Issue the read() method inside but discard the result. + /// + /// Return the number of bytes read until it encounters any Err or 0 bytes return. + fn skip(&mut self, num_bytes: uint) -> IoResult { + let mut remaining = num_bytes; + let mut buf: [u8, .. SKIP_SIZE] = unsafe { uninitialized() }; + loop { + let n_bytes_to_read = cmp::max(buf.len(), remaining); + let n_read_bytes = try!( self.read(buf.slice_to_mut(n_bytes_to_read)) ); + remaining -= n_read_bytes; + if n_read_bytes == 0 || remaining == 0 { + break; + } + } + Ok(num_bytes - remaining) + } } impl<'a> Reader for Box { @@ -1583,6 +1603,16 @@ pub trait Seek { /// stream, but the next write may cause the previous data to be filled in /// with a bit pattern. fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()>; + + /// Wrap seek(num_byes, SeekCur) to provide something similar with Reader::skip(). + /// + /// # Failure + /// + /// Fails when num_bytes doesn't fit into i64. + #[inline] + fn skip(&mut self, num_bytes: uint) -> IoResult { + self.seek(num_bytes.to_i64().unwrap(), SeekCur).and(Ok(num_bytes)) + } } /// A listener is a value that can consume itself to start listening for @@ -1908,7 +1938,8 @@ impl fmt::Show for FilePermission { #[cfg(test)] mod tests { - use super::{IoResult, Reader, MemReader, NoProgress, InvalidInput}; + use super::{IoResult, Reader, MemReader, NoProgress, InvalidInput, EndOfFile}; + use super::SKIP_SIZE; use prelude::*; use uint; @@ -1927,6 +1958,10 @@ mod tests { fn new(r: T, behavior: Vec) -> BadReader { BadReader { behavior: behavior, r: r } } + + fn simply_new(r: T) -> BadReader { + BadReader::new(r, vec![]) + } } impl Reader for BadReader { @@ -1956,20 +1991,18 @@ mod tests { #[test] fn test_read_at_least() { - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![GoodBehavior(uint::MAX)]); + let mut r = BadReader::simply_new(MemReader::new(b"hello, world!".to_vec())); let mut buf = [0u8, ..5]; assert!(r.read_at_least(1, buf).unwrap() >= 1); assert!(r.read_exact(5).unwrap().len() == 5); // read_exact uses read_at_least assert!(r.read_at_least(0, buf).is_ok()); let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(50), GoodBehavior(uint::MAX)]); + vec![BadBehavior(50)]); assert!(r.read_at_least(1, buf).unwrap() >= 1); let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(1), GoodBehavior(1), - BadBehavior(50), GoodBehavior(uint::MAX)]); + vec![BadBehavior(1), GoodBehavior(1), BadBehavior(50)]); assert!(r.read_at_least(1, buf).unwrap() >= 1); assert!(r.read_at_least(1, buf).unwrap() >= 1); @@ -1984,19 +2017,17 @@ mod tests { #[test] fn test_push_at_least() { - let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![GoodBehavior(uint::MAX)]); + let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), vec![]); let mut buf = Vec::new(); assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); assert!(r.push_at_least(0, 5, &mut buf).is_ok()); let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(50), GoodBehavior(uint::MAX)]); + vec![BadBehavior(50)]); assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); let mut r = BadReader::new(MemReader::new(b"hello, world!".to_vec()), - vec![BadBehavior(1), GoodBehavior(1), - BadBehavior(50), GoodBehavior(uint::MAX)]); + vec![BadBehavior(1), GoodBehavior(1), BadBehavior(50)]); assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); assert!(r.push_at_least(1, 5, &mut buf).unwrap() >= 1); @@ -2008,6 +2039,51 @@ mod tests { assert_eq!(r.push_at_least(5, 1, &mut buf).unwrap_err().kind, InvalidInput); } + #[test] + fn test_reader_skip() { + { + let mut r = BadReader::simply_new(MemReader::new(b"hello, world!".to_vec())); + assert_eq!(7, r.skip(7).unwrap()); + let mut buf = [0_u8, .. 10]; + assert_eq!(6, r.read(buf).unwrap()); + assert_eq!(b"world!".as_slice(), buf.as_slice()); + assert_eq!(EndOfFile, r.skip(0).unwrap_err().kind); + } + { + let mut r = BadReader::simply_new(MemReader::new( + Vec::from_fn(SKIP_SIZE + 20, |i| i as u8))); + assert_eq!(10, r.skip(10).unwrap()); + assert_eq!(10, r.read_u8().unwrap()); + assert_eq!(SKIP_SIZE, r.skip(SKIP_SIZE).unwrap()); + assert_eq!((SKIP_SIZE + 11) as u8, r.read_u8().unwrap()); + let mut buf = [0_u8, .. 10]; + assert_eq!(8, r.read(buf).unwrap()); + assert_eq!(EndOfFile, r.read(buf).unwrap_err().kind); + } + { + let mut r = BadReader::simply_new(MemReader::new( + Vec::from_fn(SKIP_SIZE + 20, |i| i as u8))); + assert_eq!(SKIP_SIZE, r.skip(SKIP_SIZE).unwrap()); + assert_eq!(SKIP_SIZE as u8, r.read_u8().unwrap()); + assert_eq!(10, r.skip(10).unwrap()); + assert_eq!((SKIP_SIZE + 11) as u8, r.read_u8().unwrap()); + let mut buf = [0_u8, .. 10]; + assert_eq!(8, r.read(buf).unwrap()); + assert_eq!(EndOfFile, r.read(buf).unwrap_err().kind); + } + { + let mut r = BadReader::simply_new(MemReader::new( + Vec::from_fn(2 * SKIP_SIZE + 20, |i| i as u8))); + assert_eq!(10, r.skip(10).unwrap()); + assert_eq!(10, r.read_u8().unwrap()); + assert_eq!(2 * SKIP_SIZE, r.skip(2 * SKIP_SIZE).unwrap()); + assert_eq!((2 * SKIP_SIZE + 11) as u8, r.read_u8().unwrap()); + let mut buf = [0_u8, .. 10]; + assert_eq!(8, r.read(buf).unwrap()); + assert_eq!(EndOfFile, r.read(buf).unwrap_err().kind); + } + } + #[test] fn test_show() { use super::*;