Skip to content

std::io: Add .lines() method to Buffer #10856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src/compiletest/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
6 changes: 1 addition & 5 deletions src/compiletest/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
22 changes: 22 additions & 0 deletions src/libstd/io/buffered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(|| {
Expand Down
24 changes: 9 additions & 15 deletions src/libstd/io/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand All @@ -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<T> {
priv reader: T,
pub struct ByteIterator<'r, T> {
priv reader: &'r mut T,
}

impl<R: Reader> ByteIterator<R> {
pub fn new(r: R) -> ByteIterator<R> {
impl<'r, R: Reader> ByteIterator<'r, R> {
pub fn new(r: &'r mut R) -> ByteIterator<'r, R> {
ByteIterator { reader: r }
}
}

impl<R> Decorator<R> for ByteIterator<R> {
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<u8> for ByteIterator<R> {
impl<'r, R: Reader> Iterator<u8> for ByteIterator<'r, R> {
#[inline]
fn next(&mut self) -> Option<u8> {
self.reader.read_byte()
Expand Down Expand Up @@ -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();
Expand All @@ -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();
Expand Down
142 changes: 105 additions & 37 deletions src/libstd/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<SocketAddr>("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

Expand Down Expand Up @@ -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;
Expand All @@ -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<Self> {
fn bytes<'r>(&'r mut self) -> extensions::ByteIterator<'r, Self> {
extensions::ByteIterator::new(self)
}

Expand Down Expand Up @@ -958,6 +981,30 @@ pub trait Stream: Reader + Writer { }

impl<T: Reader + Writer> 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
Expand Down Expand Up @@ -987,46 +1034,67 @@ 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.
///
/// # 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.
Expand Down
3 changes: 1 addition & 2 deletions src/test/bench/core-std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
}
}
}
Expand Down
9 changes: 1 addition & 8 deletions src/test/bench/shootout-k-nucleotide-pipes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
6 changes: 1 addition & 5 deletions src/test/bench/sudoku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down