diff --git a/src/compiletest/errors.rs b/src/compiletest/errors.rs index 6be92b12535..b15bf73c193 100644 --- a/src/compiletest/errors.rs +++ b/src/compiletest/errors.rs @@ -19,10 +19,7 @@ pub fn load_errors(testfile: &Path) -> ~[ExpectedError] { let mut error_patterns = ~[]; let mut rdr = BufferedReader::new(File::open(testfile).unwrap()); let mut line_num = 1u; - loop { - let ln = match rdr.read_line() { - Some(ln) => ln, None => break, - }; + for ln in rdr.lines() { error_patterns.push_all_move(parse_expected(line_num, ln)); line_num += 1u; } diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index 1966701dbde..98989a2cba2 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -107,11 +107,7 @@ fn iter_header(testfile: &Path, it: |&str| -> bool) -> bool { use std::io::File; let mut rdr = BufferedReader::new(File::open(testfile).unwrap()); - loop { - let ln = match rdr.read_line() { - Some(ln) => ln, None => break - }; - + for ln in rdr.lines() { // Assume that any directives will be found before the first // module or function. This doesn't seem to be an optimization // with a warm page cache. Maybe with a cold one. diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 9c5927f8639..04f4d50036f 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -454,6 +454,28 @@ mod test { ~[0, 1, 0, '\n' as u8, 1, '\n' as u8, 2, 3, '\n' as u8]); } + #[test] + fn test_read_line() { + let in_buf = MemReader::new(bytes!("a\nb\nc").to_owned()); + let mut reader = BufferedReader::with_capacity(2, in_buf); + assert_eq!(reader.read_line(), Some(~"a\n")); + assert_eq!(reader.read_line(), Some(~"b\n")); + assert_eq!(reader.read_line(), Some(~"c")); + assert_eq!(reader.read_line(), None); + } + + #[test] + fn test_lines() { + let in_buf = MemReader::new(bytes!("a\nb\nc").to_owned()); + let mut reader = BufferedReader::with_capacity(2, in_buf); + let mut it = reader.lines(); + assert_eq!(it.next(), Some(~"a\n")); + assert_eq!(it.next(), Some(~"b\n")); + assert_eq!(it.next(), Some(~"c")); + assert_eq!(it.next(), None); + } + + #[bench] fn bench_buffered_reader(bh: &mut Harness) { bh.iter(|| { diff --git a/src/libstd/io/extensions.rs b/src/libstd/io/extensions.rs index 564e664027f..a7361fa8c37 100644 --- a/src/libstd/io/extensions.rs +++ b/src/libstd/io/extensions.rs @@ -15,7 +15,7 @@ use iter::Iterator; use option::Option; -use io::{Reader, Decorator}; +use io::Reader; /// An iterator that reads a single byte on each iteration, /// until `.read_byte()` returns `None`. @@ -31,23 +31,17 @@ use io::{Reader, Decorator}; /// Raises the same conditions as the `read` method, for /// each call to its `.next()` method. /// Yields `None` if the condition is handled. -pub struct ByteIterator { - priv reader: T, +pub struct ByteIterator<'r, T> { + priv reader: &'r mut T, } -impl ByteIterator { - pub fn new(r: R) -> ByteIterator { +impl<'r, R: Reader> ByteIterator<'r, R> { + pub fn new(r: &'r mut R) -> ByteIterator<'r, R> { ByteIterator { reader: r } } } -impl Decorator for ByteIterator { - fn inner(self) -> R { self.reader } - fn inner_ref<'a>(&'a self) -> &'a R { &self.reader } - fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { &mut self.reader } -} - -impl<'self, R: Reader> Iterator for ByteIterator { +impl<'r, R: Reader> Iterator for ByteIterator<'r, R> { #[inline] fn next(&mut self) -> Option { self.reader.read_byte() @@ -285,7 +279,7 @@ mod test { #[test] fn bytes_0_bytes() { - let reader = InitialZeroByteReader { + let mut reader = InitialZeroByteReader { count: 0, }; let byte = reader.bytes().next(); @@ -294,14 +288,14 @@ mod test { #[test] fn bytes_eof() { - let reader = EofReader; + let mut reader = EofReader; let byte = reader.bytes().next(); assert!(byte == None); } #[test] fn bytes_error() { - let reader = ErroringReader; + let mut reader = ErroringReader; let mut it = reader.bytes(); io_error::cond.trap(|_| ()).inside(|| { let byte = it.next(); diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index d5e216e2426..77bdf866338 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -25,41 +25,63 @@ Some examples of obvious things you might want to do * Read lines from stdin - for stdin().each_line |line| { - println(line) + ```rust + let mut stdin = BufferedReader::new(stdin()); + for line in stdin.lines() { + print(line); } + ``` -* Read a complete file to a string, (converting newlines?) +* Read a complete file - let contents = File::open("message.txt").read_to_str(); // read_to_str?? + ```rust + let contents = File::open(&Path::new("message.txt")).read_to_end(); + ``` * Write a line to a file - let file = File::open("message.txt", Create, Write); - file.write_line("hello, file!"); + ```rust + let mut file = File::create(&Path::new("message.txt")); + file.write(bytes!("hello, file!\n")); + ``` * Iterate over the lines of a file - File::open("message.txt").each_line(|line| { - println(line) - }) + ```rust + let path = Path::new("message.txt"); + let mut file = BufferedReader::new(File::open(&path)); + for line in file.lines() { + print(line); + } + ``` * Pull the lines of a file into a vector of strings - let lines = File::open("message.txt").lines().to_vec(); + ```rust + let path = Path::new("message.txt"); + let mut file = BufferedReader::new(File::open(&path)); + let lines: ~[~str] = file.lines().collect(); + ``` * Make an simple HTTP request + XXX This needs more improvement: TcpStream constructor taking &str, + `write_str` and `write_line` methods. - let socket = TcpStream::open("localhost:8080"); - socket.write_line("GET / HTTP/1.0"); - socket.write_line(""); + ```rust + let addr = from_str::("127.0.0.1:8080").unwrap(); + let mut socket = TcpStream::connect(addr).unwrap(); + socket.write(bytes!("GET / HTTP/1.0\n\n")); let response = socket.read_to_end(); + ``` * Connect based on URL? Requires thinking about where the URL type lives and how to make protocol handlers extensible, e.g. the "tcp" protocol yields a `TcpStream`. + XXX this is not implemented now. - connect("tcp://localhost:8080"); + ```rust + // connect("tcp://localhost:8080"); + ``` # Terms @@ -535,7 +557,8 @@ pub trait Reader { /// /// # Failure /// - /// Raises the same conditions as the `read` method. + /// Raises the same conditions as the `read` method except for + /// `EndOfFile` which is swallowed. fn read_to_end(&mut self) -> ~[u8] { let mut buf = vec::with_capacity(DEFAULT_BUF_SIZE); let mut keep_reading = true; @@ -561,7 +584,7 @@ pub trait Reader { /// Raises the same conditions as the `read` method, for /// each call to its `.next()` method. /// Ends the iteration if the condition is handled. - fn bytes(self) -> extensions::ByteIterator { + fn bytes<'r>(&'r mut self) -> extensions::ByteIterator<'r, Self> { extensions::ByteIterator::new(self) } @@ -958,6 +981,30 @@ pub trait Stream: Reader + Writer { } impl Stream for T {} +/// An iterator that reads a line on each iteration, +/// until `.read_line()` returns `None`. +/// +/// # Notes about the Iteration Protocol +/// +/// The `LineIterator` may yield `None` and thus terminate +/// an iteration, but continue to yield elements if iteration +/// is attempted again. +/// +/// # Failure +/// +/// Raises the same conditions as the `read` method except for `EndOfFile` +/// which is swallowed. +/// Iteration yields `None` if the condition is handled. +struct LineIterator<'r, T> { + priv buffer: &'r mut T, +} + +impl<'r, T: Buffer> Iterator<~str> for LineIterator<'r, T> { + fn next(&mut self) -> Option<~str> { + self.buffer.read_line() + } +} + /// A Buffer is a type of reader which has some form of internal buffering to /// allow certain kinds of reading operations to be more optimized than others. /// This type extends the `Reader` trait with a few methods that are not @@ -987,13 +1034,26 @@ pub trait Buffer: Reader { /// /// # Failure /// - /// This function will raise on the `io_error` condition if a read error is - /// encountered. The task will also fail if sequence of bytes leading up to + /// This function will raise on the `io_error` condition (except for + /// `EndOfFile` which is swallowed) if a read error is encountered. + /// The task will also fail if sequence of bytes leading up to /// the newline character are not valid utf-8. fn read_line(&mut self) -> Option<~str> { self.read_until('\n' as u8).map(str::from_utf8_owned) } + /// Create an iterator that reads a line on each iteration until EOF. + /// + /// # Failure + /// + /// Iterator raises the same conditions as the `read` method + /// except for `EndOfFile`. + fn lines<'r>(&'r mut self) -> LineIterator<'r, Self> { + LineIterator { + buffer: self, + } + } + /// Reads a sequence of bytes leading up to a specified delimeter. Once the /// specified byte is encountered, reading ceases and the bytes up to and /// including the delimiter are returned. @@ -1001,32 +1061,40 @@ pub trait Buffer: Reader { /// # Failure /// /// This function will raise on the `io_error` condition if a read error is - /// encountered. + /// encountered, except that `EndOfFile` is swallowed. fn read_until(&mut self, byte: u8) -> Option<~[u8]> { let mut res = ~[]; - let mut used; - loop { - { - let available = self.fill(); - match available.iter().position(|&b| b == byte) { - Some(i) => { - res.push_all(available.slice_to(i + 1)); - used = i + 1; - break - } - None => { - res.push_all(available); - used = available.len(); + + io_error::cond.trap(|e| { + if e.kind != EndOfFile { + io_error::cond.raise(e); + } + }).inside(|| { + let mut used; + loop { + { + let available = self.fill(); + match available.iter().position(|&b| b == byte) { + Some(i) => { + res.push_all(available.slice_to(i + 1)); + used = i + 1; + break + } + None => { + res.push_all(available); + used = available.len(); + } } } - } - if used == 0 { - break + if used == 0 { + break + } + self.consume(used); } self.consume(used); - } - self.consume(used); + }); return if res.len() == 0 {None} else {Some(res)}; + } /// Reads the next utf8-encoded character from the underlying stream. diff --git a/src/test/bench/core-std.rs b/src/test/bench/core-std.rs index c7036f8d9db..9be462f736b 100644 --- a/src/test/bench/core-std.rs +++ b/src/test/bench/core-std.rs @@ -77,8 +77,7 @@ fn read_line() { for _ in range(0, 3) { let mut reader = BufferedReader::new(File::open(&path).unwrap()); - while !reader.eof() { - reader.read_line(); + for _line in reader.lines() { } } } diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs index ac2b1958f93..a12eac50852 100644 --- a/src/test/bench/shootout-k-nucleotide-pipes.rs +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -188,14 +188,7 @@ fn main() { // reading the sequence of interest let mut proc_mode = false; - loop { - let line = { - let _guard = io::ignore_io_error(); - match rdr.read_line() { - Some(ln) => ln, - None => break, - } - }; + for line in rdr.lines() { let line = line.trim().to_owned(); if line.len() == 0u { continue; } diff --git a/src/test/bench/sudoku.rs b/src/test/bench/sudoku.rs index 497b6a392a5..39bcfde2826 100644 --- a/src/test/bench/sudoku.rs +++ b/src/test/bench/sudoku.rs @@ -18,7 +18,6 @@ use std::io; use std::io::stdio::StdReader; use std::io::buffered::BufferedReader; use std::os; -use std::uint; use std::unstable::intrinsics::cttz16; use std::vec; @@ -72,10 +71,7 @@ impl Sudoku { assert!(reader.read_line().unwrap() == ~"9,9"); /* assert first line is exactly "9,9" */ let mut g = vec::from_fn(10u, { |_i| ~[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] }); - loop { - let line = match reader.read_line() { - Some(ln) => ln, None => break - }; + for line in reader.lines() { let comps: ~[&str] = line.trim().split(',').collect(); if comps.len() == 3u {