Skip to content

Commit 5c2f8aa

Browse files
committed
auto merge of #10856 : klutzy/rust/buf-reader-lines, r=alexcrichton
The `.lines()` method creates an iterator which yields line with trailing ' '. (So it is slightly different to `StrSlice.lines()`; I don't know if it's worth to synchronize them.)
2 parents fff03a5 + 5a93d12 commit 5c2f8aa

File tree

8 files changed

+141
-76
lines changed

8 files changed

+141
-76
lines changed

src/compiletest/errors.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ pub fn load_errors(testfile: &Path) -> ~[ExpectedError] {
1919
let mut error_patterns = ~[];
2020
let mut rdr = BufferedReader::new(File::open(testfile).unwrap());
2121
let mut line_num = 1u;
22-
loop {
23-
let ln = match rdr.read_line() {
24-
Some(ln) => ln, None => break,
25-
};
22+
for ln in rdr.lines() {
2623
error_patterns.push_all_move(parse_expected(line_num, ln));
2724
line_num += 1u;
2825
}

src/compiletest/header.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,7 @@ fn iter_header(testfile: &Path, it: |&str| -> bool) -> bool {
107107
use std::io::File;
108108

109109
let mut rdr = BufferedReader::new(File::open(testfile).unwrap());
110-
loop {
111-
let ln = match rdr.read_line() {
112-
Some(ln) => ln, None => break
113-
};
114-
110+
for ln in rdr.lines() {
115111
// Assume that any directives will be found before the first
116112
// module or function. This doesn't seem to be an optimization
117113
// with a warm page cache. Maybe with a cold one.

src/libstd/io/buffered.rs

+22
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,28 @@ mod test {
454454
~[0, 1, 0, '\n' as u8, 1, '\n' as u8, 2, 3, '\n' as u8]);
455455
}
456456

457+
#[test]
458+
fn test_read_line() {
459+
let in_buf = MemReader::new(bytes!("a\nb\nc").to_owned());
460+
let mut reader = BufferedReader::with_capacity(2, in_buf);
461+
assert_eq!(reader.read_line(), Some(~"a\n"));
462+
assert_eq!(reader.read_line(), Some(~"b\n"));
463+
assert_eq!(reader.read_line(), Some(~"c"));
464+
assert_eq!(reader.read_line(), None);
465+
}
466+
467+
#[test]
468+
fn test_lines() {
469+
let in_buf = MemReader::new(bytes!("a\nb\nc").to_owned());
470+
let mut reader = BufferedReader::with_capacity(2, in_buf);
471+
let mut it = reader.lines();
472+
assert_eq!(it.next(), Some(~"a\n"));
473+
assert_eq!(it.next(), Some(~"b\n"));
474+
assert_eq!(it.next(), Some(~"c"));
475+
assert_eq!(it.next(), None);
476+
}
477+
478+
457479
#[bench]
458480
fn bench_buffered_reader(bh: &mut Harness) {
459481
bh.iter(|| {

src/libstd/io/extensions.rs

+9-15
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
use iter::Iterator;
1717
use option::Option;
18-
use io::{Reader, Decorator};
18+
use io::Reader;
1919

2020
/// An iterator that reads a single byte on each iteration,
2121
/// until `.read_byte()` returns `None`.
@@ -31,23 +31,17 @@ use io::{Reader, Decorator};
3131
/// Raises the same conditions as the `read` method, for
3232
/// each call to its `.next()` method.
3333
/// Yields `None` if the condition is handled.
34-
pub struct ByteIterator<T> {
35-
priv reader: T,
34+
pub struct ByteIterator<'r, T> {
35+
priv reader: &'r mut T,
3636
}
3737

38-
impl<R: Reader> ByteIterator<R> {
39-
pub fn new(r: R) -> ByteIterator<R> {
38+
impl<'r, R: Reader> ByteIterator<'r, R> {
39+
pub fn new(r: &'r mut R) -> ByteIterator<'r, R> {
4040
ByteIterator { reader: r }
4141
}
4242
}
4343

44-
impl<R> Decorator<R> for ByteIterator<R> {
45-
fn inner(self) -> R { self.reader }
46-
fn inner_ref<'a>(&'a self) -> &'a R { &self.reader }
47-
fn inner_mut_ref<'a>(&'a mut self) -> &'a mut R { &mut self.reader }
48-
}
49-
50-
impl<'self, R: Reader> Iterator<u8> for ByteIterator<R> {
44+
impl<'r, R: Reader> Iterator<u8> for ByteIterator<'r, R> {
5145
#[inline]
5246
fn next(&mut self) -> Option<u8> {
5347
self.reader.read_byte()
@@ -285,7 +279,7 @@ mod test {
285279

286280
#[test]
287281
fn bytes_0_bytes() {
288-
let reader = InitialZeroByteReader {
282+
let mut reader = InitialZeroByteReader {
289283
count: 0,
290284
};
291285
let byte = reader.bytes().next();
@@ -294,14 +288,14 @@ mod test {
294288

295289
#[test]
296290
fn bytes_eof() {
297-
let reader = EofReader;
291+
let mut reader = EofReader;
298292
let byte = reader.bytes().next();
299293
assert!(byte == None);
300294
}
301295

302296
#[test]
303297
fn bytes_error() {
304-
let reader = ErroringReader;
298+
let mut reader = ErroringReader;
305299
let mut it = reader.bytes();
306300
io_error::cond.trap(|_| ()).inside(|| {
307301
let byte = it.next();

src/libstd/io/mod.rs

+105-37
Original file line numberDiff line numberDiff line change
@@ -25,41 +25,63 @@ Some examples of obvious things you might want to do
2525
2626
* Read lines from stdin
2727
28-
for stdin().each_line |line| {
29-
println(line)
28+
```rust
29+
let mut stdin = BufferedReader::new(stdin());
30+
for line in stdin.lines() {
31+
print(line);
3032
}
33+
```
3134
32-
* Read a complete file to a string, (converting newlines?)
35+
* Read a complete file
3336
34-
let contents = File::open("message.txt").read_to_str(); // read_to_str??
37+
```rust
38+
let contents = File::open(&Path::new("message.txt")).read_to_end();
39+
```
3540
3641
* Write a line to a file
3742
38-
let file = File::open("message.txt", Create, Write);
39-
file.write_line("hello, file!");
43+
```rust
44+
let mut file = File::create(&Path::new("message.txt"));
45+
file.write(bytes!("hello, file!\n"));
46+
```
4047
4148
* Iterate over the lines of a file
4249
43-
File::open("message.txt").each_line(|line| {
44-
println(line)
45-
})
50+
```rust
51+
let path = Path::new("message.txt");
52+
let mut file = BufferedReader::new(File::open(&path));
53+
for line in file.lines() {
54+
print(line);
55+
}
56+
```
4657
4758
* Pull the lines of a file into a vector of strings
4859
49-
let lines = File::open("message.txt").lines().to_vec();
60+
```rust
61+
let path = Path::new("message.txt");
62+
let mut file = BufferedReader::new(File::open(&path));
63+
let lines: ~[~str] = file.lines().collect();
64+
```
5065
5166
* Make an simple HTTP request
67+
XXX This needs more improvement: TcpStream constructor taking &str,
68+
`write_str` and `write_line` methods.
5269
53-
let socket = TcpStream::open("localhost:8080");
54-
socket.write_line("GET / HTTP/1.0");
55-
socket.write_line("");
70+
```rust
71+
let addr = from_str::<SocketAddr>("127.0.0.1:8080").unwrap();
72+
let mut socket = TcpStream::connect(addr).unwrap();
73+
socket.write(bytes!("GET / HTTP/1.0\n\n"));
5674
let response = socket.read_to_end();
75+
```
5776
5877
* Connect based on URL? Requires thinking about where the URL type lives
5978
and how to make protocol handlers extensible, e.g. the "tcp" protocol
6079
yields a `TcpStream`.
80+
XXX this is not implemented now.
6181
62-
connect("tcp://localhost:8080");
82+
```rust
83+
// connect("tcp://localhost:8080");
84+
```
6385
6486
# Terms
6587
@@ -535,7 +557,8 @@ pub trait Reader {
535557
///
536558
/// # Failure
537559
///
538-
/// Raises the same conditions as the `read` method.
560+
/// Raises the same conditions as the `read` method except for
561+
/// `EndOfFile` which is swallowed.
539562
fn read_to_end(&mut self) -> ~[u8] {
540563
let mut buf = vec::with_capacity(DEFAULT_BUF_SIZE);
541564
let mut keep_reading = true;
@@ -561,7 +584,7 @@ pub trait Reader {
561584
/// Raises the same conditions as the `read` method, for
562585
/// each call to its `.next()` method.
563586
/// Ends the iteration if the condition is handled.
564-
fn bytes(self) -> extensions::ByteIterator<Self> {
587+
fn bytes<'r>(&'r mut self) -> extensions::ByteIterator<'r, Self> {
565588
extensions::ByteIterator::new(self)
566589
}
567590

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

959982
impl<T: Reader + Writer> Stream for T {}
960983

984+
/// An iterator that reads a line on each iteration,
985+
/// until `.read_line()` returns `None`.
986+
///
987+
/// # Notes about the Iteration Protocol
988+
///
989+
/// The `LineIterator` may yield `None` and thus terminate
990+
/// an iteration, but continue to yield elements if iteration
991+
/// is attempted again.
992+
///
993+
/// # Failure
994+
///
995+
/// Raises the same conditions as the `read` method except for `EndOfFile`
996+
/// which is swallowed.
997+
/// Iteration yields `None` if the condition is handled.
998+
struct LineIterator<'r, T> {
999+
priv buffer: &'r mut T,
1000+
}
1001+
1002+
impl<'r, T: Buffer> Iterator<~str> for LineIterator<'r, T> {
1003+
fn next(&mut self) -> Option<~str> {
1004+
self.buffer.read_line()
1005+
}
1006+
}
1007+
9611008
/// A Buffer is a type of reader which has some form of internal buffering to
9621009
/// allow certain kinds of reading operations to be more optimized than others.
9631010
/// This type extends the `Reader` trait with a few methods that are not
@@ -987,46 +1034,67 @@ pub trait Buffer: Reader {
9871034
///
9881035
/// # Failure
9891036
///
990-
/// This function will raise on the `io_error` condition if a read error is
991-
/// encountered. The task will also fail if sequence of bytes leading up to
1037+
/// This function will raise on the `io_error` condition (except for
1038+
/// `EndOfFile` which is swallowed) if a read error is encountered.
1039+
/// The task will also fail if sequence of bytes leading up to
9921040
/// the newline character are not valid utf-8.
9931041
fn read_line(&mut self) -> Option<~str> {
9941042
self.read_until('\n' as u8).map(str::from_utf8_owned)
9951043
}
9961044

1045+
/// Create an iterator that reads a line on each iteration until EOF.
1046+
///
1047+
/// # Failure
1048+
///
1049+
/// Iterator raises the same conditions as the `read` method
1050+
/// except for `EndOfFile`.
1051+
fn lines<'r>(&'r mut self) -> LineIterator<'r, Self> {
1052+
LineIterator {
1053+
buffer: self,
1054+
}
1055+
}
1056+
9971057
/// Reads a sequence of bytes leading up to a specified delimeter. Once the
9981058
/// specified byte is encountered, reading ceases and the bytes up to and
9991059
/// including the delimiter are returned.
10001060
///
10011061
/// # Failure
10021062
///
10031063
/// This function will raise on the `io_error` condition if a read error is
1004-
/// encountered.
1064+
/// encountered, except that `EndOfFile` is swallowed.
10051065
fn read_until(&mut self, byte: u8) -> Option<~[u8]> {
10061066
let mut res = ~[];
1007-
let mut used;
1008-
loop {
1009-
{
1010-
let available = self.fill();
1011-
match available.iter().position(|&b| b == byte) {
1012-
Some(i) => {
1013-
res.push_all(available.slice_to(i + 1));
1014-
used = i + 1;
1015-
break
1016-
}
1017-
None => {
1018-
res.push_all(available);
1019-
used = available.len();
1067+
1068+
io_error::cond.trap(|e| {
1069+
if e.kind != EndOfFile {
1070+
io_error::cond.raise(e);
1071+
}
1072+
}).inside(|| {
1073+
let mut used;
1074+
loop {
1075+
{
1076+
let available = self.fill();
1077+
match available.iter().position(|&b| b == byte) {
1078+
Some(i) => {
1079+
res.push_all(available.slice_to(i + 1));
1080+
used = i + 1;
1081+
break
1082+
}
1083+
None => {
1084+
res.push_all(available);
1085+
used = available.len();
1086+
}
10201087
}
10211088
}
1022-
}
1023-
if used == 0 {
1024-
break
1089+
if used == 0 {
1090+
break
1091+
}
1092+
self.consume(used);
10251093
}
10261094
self.consume(used);
1027-
}
1028-
self.consume(used);
1095+
});
10291096
return if res.len() == 0 {None} else {Some(res)};
1097+
10301098
}
10311099

10321100
/// Reads the next utf8-encoded character from the underlying stream.

src/test/bench/core-std.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ fn read_line() {
7777

7878
for _ in range(0, 3) {
7979
let mut reader = BufferedReader::new(File::open(&path).unwrap());
80-
while !reader.eof() {
81-
reader.read_line();
80+
for _line in reader.lines() {
8281
}
8382
}
8483
}

src/test/bench/shootout-k-nucleotide-pipes.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,7 @@ fn main() {
188188
// reading the sequence of interest
189189
let mut proc_mode = false;
190190
191-
loop {
192-
let line = {
193-
let _guard = io::ignore_io_error();
194-
match rdr.read_line() {
195-
Some(ln) => ln,
196-
None => break,
197-
}
198-
};
191+
for line in rdr.lines() {
199192
let line = line.trim().to_owned();
200193
201194
if line.len() == 0u { continue; }

src/test/bench/sudoku.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use std::io;
1818
use std::io::stdio::StdReader;
1919
use std::io::buffered::BufferedReader;
2020
use std::os;
21-
use std::uint;
2221
use std::unstable::intrinsics::cttz16;
2322
use std::vec;
2423

@@ -72,10 +71,7 @@ impl Sudoku {
7271
assert!(reader.read_line().unwrap() == ~"9,9"); /* assert first line is exactly "9,9" */
7372
7473
let mut g = vec::from_fn(10u, { |_i| ~[0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8] });
75-
loop {
76-
let line = match reader.read_line() {
77-
Some(ln) => ln, None => break
78-
};
74+
for line in reader.lines() {
7975
let comps: ~[&str] = line.trim().split(',').collect();
8076
8177
if comps.len() == 3u {

0 commit comments

Comments
 (0)