Skip to content

Commit 0ede2ea

Browse files
committed
auto merge of #9749 : alexcrichton/rust/less-io, r=brson
This implements a number of the baby steps needed to start eliminating everything inside of `std::io`. It turns out that there are a *lot* of users of that module, so I'm going to try to tackle them separately instead of bringing down the whole system all at once. This pull implements a large amount of unimplemented functionality inside of `std::rt::io` including: * Native file I/O (file descriptors, *FILE) * Native stdio (through the native file descriptors) * Native processes (extracted from `std::run`) I also found that there are a number of users of `std::io` which desire to read an input line-by-line, so I added an implementation of `read_until` and `read_line` to `BufferedReader`. With all of these changes in place, I started to axe various usages of `std::io`. There's a lot of one-off uses here-and-there, but the major use-case remaining that doesn't have a fantastic solution is `extra::json`. I ran into a few compiler bugs when attempting to remove that, so I figured I'd come back to it later instead. There is one fairly major change in this pull, and it's moving from native stdio to uv stdio via `print` and `println`. Unfortunately logging still goes through native I/O (via `dumb_println`). This is going to need some thinking, because I still want the goal of logging/printing to be 0 allocations, and this is not possible if `io::stdio::stderr()` is called on each log message. Instead I think that this may need to be cached as the `logger` field inside the `Task` struct, but that will require a little more workings to get right (this is also a similar problem for print/println, do we cache `stdout()` to not have to re-create it every time?).
2 parents 34d123d + 4137471 commit 0ede2ea

21 files changed

+1382
-1004
lines changed

src/compiletest/errors.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,29 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use std::io;
12-
1311
pub struct ExpectedError { line: uint, kind: ~str, msg: ~str }
1412

1513
// Load any test directives embedded in the file
1614
pub fn load_errors(testfile: &Path) -> ~[ExpectedError] {
15+
use std::rt::io::Open;
16+
use std::rt::io::file::FileInfo;
17+
use std::rt::io::buffered::BufferedReader;
18+
1719
let mut error_patterns = ~[];
18-
let rdr = io::file_reader(testfile).unwrap();
20+
let mut rdr = BufferedReader::new(testfile.open_reader(Open).unwrap());
1921
let mut line_num = 1u;
20-
while !rdr.eof() {
21-
let ln = rdr.read_line();
22+
loop {
23+
let ln = match rdr.read_line() {
24+
Some(ln) => ln, None => break,
25+
};
2226
error_patterns.push_all_move(parse_expected(line_num, ln));
2327
line_num += 1u;
2428
}
2529
return error_patterns;
2630
}
2731

2832
fn parse_expected(line_num: uint, line: ~str) -> ~[ExpectedError] {
33+
let line = line.trim();
2934
let error_tag = ~"//~";
3035
let mut idx;
3136
match line.find_str(error_tag) {

src/compiletest/header.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ use common::config;
1212
use common;
1313
use util;
1414

15-
use std::io;
16-
1715
pub struct TestProps {
1816
// Lines that should be expected, in order, on standard out
1917
error_patterns: ~[~str],
@@ -104,17 +102,23 @@ pub fn is_test_ignored(config: &config, testfile: &Path) -> bool {
104102
!val
105103
}
106104

107-
fn iter_header(testfile: &Path, it: &fn(~str) -> bool) -> bool {
108-
let rdr = io::file_reader(testfile).unwrap();
109-
while !rdr.eof() {
110-
let ln = rdr.read_line();
105+
fn iter_header(testfile: &Path, it: &fn(&str) -> bool) -> bool {
106+
use std::rt::io::Open;
107+
use std::rt::io::file::FileInfo;
108+
use std::rt::io::buffered::BufferedReader;
109+
110+
let mut rdr = BufferedReader::new(testfile.open_reader(Open).unwrap());
111+
loop {
112+
let ln = match rdr.read_line() {
113+
Some(ln) => ln, None => break
114+
};
111115

112116
// Assume that any directives will be found before the first
113117
// module or function. This doesn't seem to be an optimization
114118
// with a warm page cache. Maybe with a cold one.
115119
if ln.starts_with("fn") || ln.starts_with("mod") {
116120
return true;
117-
} else { if !(it(ln)) { return false; } }
121+
} else { if !(it(ln.trim())) { return false; } }
118122
}
119123
return true;
120124
}

src/compiletest/procsrv.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub fn run(lib_path: &str,
5757
});
5858

5959
for input in input.iter() {
60-
proc.input().write_str(*input);
60+
proc.input().write(input.as_bytes());
6161
}
6262
let output = proc.finish_with_output();
6363

src/libstd/cleanup.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,6 @@ fn debug_mem() -> bool {
6868
/// Destroys all managed memory (i.e. @ boxes) held by the current task.
6969
pub unsafe fn annihilate() {
7070
use rt::local_heap::local_free;
71-
use io::WriterUtil;
72-
use io;
73-
use libc;
7471
use sys;
7572
use managed;
7673

@@ -126,14 +123,10 @@ pub unsafe fn annihilate() {
126123

127124
if debug_mem() {
128125
// We do logging here w/o allocation.
129-
let dbg = libc::STDERR_FILENO as io::fd_t;
130-
dbg.write_str("annihilator stats:");
131-
dbg.write_str("\n total_boxes: ");
132-
dbg.write_uint(stats.n_total_boxes);
133-
dbg.write_str("\n unique_boxes: ");
134-
dbg.write_uint(stats.n_unique_boxes);
135-
dbg.write_str("\n bytes_freed: ");
136-
dbg.write_uint(stats.n_bytes_freed);
137-
dbg.write_str("\n");
126+
rterrln!("annihilator stats:\n \
127+
total boxes: {}\n \
128+
unique boxes: {}\n \
129+
bytes freed: {}",
130+
stats.n_total_boxes, stats.n_unique_boxes, stats.n_bytes_freed);
138131
}
139132
}

src/libstd/io.rs

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,14 +1233,6 @@ impl Writer for *libc::FILE {
12331233
}
12341234
}
12351235

1236-
pub fn FILE_writer(f: *libc::FILE, cleanup: bool) -> @Writer {
1237-
if cleanup {
1238-
@Wrapper { base: f, cleanup: FILERes::new(f) } as @Writer
1239-
} else {
1240-
@f as @Writer
1241-
}
1242-
}
1243-
12441236
impl Writer for fd_t {
12451237
fn write(&self, v: &[u8]) {
12461238
#[fixed_stack_segment]; #[inline(never)];
@@ -1618,25 +1610,6 @@ pub fn file_writer(path: &Path, flags: &[FileFlag]) -> Result<@Writer, ~str> {
16181610
mk_file_writer(path, flags).and_then(|w| Ok(w))
16191611
}
16201612

1621-
1622-
// FIXME: fileflags // #2004
1623-
pub fn buffered_file_writer(path: &Path) -> Result<@Writer, ~str> {
1624-
#[fixed_stack_segment]; #[inline(never)];
1625-
1626-
unsafe {
1627-
let f = do path.with_c_str |pathbuf| {
1628-
do "w".with_c_str |modebuf| {
1629-
libc::fopen(pathbuf, modebuf)
1630-
}
1631-
};
1632-
return if f as uint == 0u {
1633-
Err(~"error opening " + path.to_str())
1634-
} else {
1635-
Ok(FILE_writer(f, true))
1636-
}
1637-
}
1638-
}
1639-
16401613
// FIXME (#2004) it would be great if this could be a const
16411614
// FIXME (#2004) why are these different from the way stdin() is
16421615
// implemented?
@@ -2086,16 +2059,6 @@ mod tests {
20862059
}
20872060
}
20882061
2089-
#[test]
2090-
fn buffered_file_writer_bad_name() {
2091-
match io::buffered_file_writer(&Path("?/?")) {
2092-
Err(e) => {
2093-
assert!(e.starts_with("error opening"));
2094-
}
2095-
Ok(_) => fail2!()
2096-
}
2097-
}
2098-
20992062
#[test]
21002063
fn bytes_buffer_overwrite() {
21012064
let wr = BytesWriter::new();

src/libstd/macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
macro_rules! rterrln (
1515
($($arg:tt)*) => ( {
16-
::rt::util::dumb_println(format!($($arg)*));
16+
format_args!(::rt::util::dumb_println, $($arg)*)
1717
} )
1818
)
1919

src/libstd/prelude.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub use option::{Option, Some, None};
3939
pub use result::{Result, Ok, Err};
4040

4141
// Reexported functions
42-
pub use io::{print, println};
42+
pub use rt::io::stdio::{print, println};
4343
pub use iter::range;
4444
pub use from_str::from_str;
4545

src/libstd/rt/borrowck.rs

Lines changed: 6 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@
99
// except according to those terms.
1010

1111
use cell::Cell;
12-
use c_str::ToCStr;
13-
use cast::transmute;
14-
use io::{Writer, WriterUtil};
15-
use io;
16-
use libc::{c_char, size_t, STDERR_FILENO};
12+
use c_str::{ToCStr, CString};
13+
use libc::{c_char, size_t};
1714
use option::{Option, None, Some};
1815
use ptr::RawPtr;
1916
use rt::env;
@@ -113,51 +110,10 @@ unsafe fn debug_borrow<T,P:RawPtr<T>>(tag: &'static str,
113110
new_bits: uint,
114111
filename: *c_char,
115112
line: size_t) {
116-
let dbg = STDERR_FILENO as io::fd_t;
117-
dbg.write_str(tag);
118-
dbg.write_hex(p.to_uint());
119-
dbg.write_str(" ");
120-
dbg.write_hex(old_bits);
121-
dbg.write_str(" ");
122-
dbg.write_hex(new_bits);
123-
dbg.write_str(" ");
124-
dbg.write_cstr(filename);
125-
dbg.write_str(":");
126-
dbg.write_hex(line as uint);
127-
dbg.write_str("\n");
128-
}
129-
}
130-
131-
trait DebugPrints {
132-
fn write_hex(&self, val: uint);
133-
unsafe fn write_cstr(&self, str: *c_char);
134-
}
135-
136-
impl DebugPrints for io::fd_t {
137-
fn write_hex(&self, mut i: uint) {
138-
let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
139-
'9', 'a', 'b', 'c', 'd', 'e', 'f'];
140-
static UINT_NIBBLES: uint = ::uint::bytes << 1;
141-
let mut buffer = [0_u8, ..UINT_NIBBLES+1];
142-
let mut c = UINT_NIBBLES;
143-
while c > 0 {
144-
c -= 1;
145-
buffer[c] = letters[i & 0xF] as u8;
146-
i >>= 4;
147-
}
148-
self.write(buffer.slice(0, UINT_NIBBLES));
149-
}
150-
151-
unsafe fn write_cstr(&self, p: *c_char) {
152-
#[fixed_stack_segment]; #[inline(never)];
153-
use libc::strlen;
154-
use vec;
155-
156-
let len = strlen(p);
157-
let p: *u8 = transmute(p);
158-
do vec::raw::buf_as_slice(p, len as uint) |s| {
159-
self.write(s);
160-
}
113+
let filename = CString::new(filename, false);
114+
rterrln!("{}{:#x} {:x} {:x} {}:{}",
115+
tag, p.to_uint(), old_bits, new_bits,
116+
filename.as_str().unwrap(), line);
161117
}
162118
}
163119

src/libstd/rt/io/buffered.rs

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ use prelude::*;
5555

5656
use num;
5757
use vec;
58+
use str;
5859
use super::{Reader, Writer, Stream, Decorator};
5960

6061
// libuv recommends 64k buffers to maximize throughput
@@ -84,23 +85,69 @@ impl<R: Reader> BufferedReader<R> {
8485
pub fn new(inner: R) -> BufferedReader<R> {
8586
BufferedReader::with_capacity(DEFAULT_CAPACITY, inner)
8687
}
87-
}
8888

89-
impl<R: Reader> Reader for BufferedReader<R> {
90-
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
89+
/// Reads the next line of input, interpreted as a sequence of utf-8
90+
/// encoded unicode codepoints. If a newline is encountered, then the
91+
/// newline is contained in the returned string.
92+
pub fn read_line(&mut self) -> Option<~str> {
93+
self.read_until('\n' as u8).map(str::from_utf8_owned)
94+
}
95+
96+
/// Reads a sequence of bytes leading up to a specified delimeter. Once the
97+
/// specified byte is encountered, reading ceases and the bytes up to and
98+
/// including the delimiter are returned.
99+
pub fn read_until(&mut self, byte: u8) -> Option<~[u8]> {
100+
let mut res = ~[];
101+
let mut used;
102+
loop {
103+
{
104+
let available = self.fill_buffer();
105+
match available.iter().position(|&b| b == byte) {
106+
Some(i) => {
107+
res.push_all(available.slice_to(i + 1));
108+
used = i + 1;
109+
break
110+
}
111+
None => {
112+
res.push_all(available);
113+
used = available.len();
114+
}
115+
}
116+
}
117+
if used == 0 {
118+
break
119+
}
120+
self.pos += used;
121+
}
122+
self.pos += used;
123+
return if res.len() == 0 {None} else {Some(res)};
124+
}
125+
126+
fn fill_buffer<'a>(&'a mut self) -> &'a [u8] {
91127
if self.pos == self.cap {
92128
match self.inner.read(self.buf) {
93129
Some(cap) => {
94130
self.pos = 0;
95131
self.cap = cap;
96132
}
97-
None => return None
133+
None => {}
98134
}
99135
}
136+
return self.buf.slice(self.pos, self.cap);
137+
}
138+
}
100139

101-
let src = self.buf.slice(self.pos, self.cap);
102-
let nread = num::min(src.len(), buf.len());
103-
vec::bytes::copy_memory(buf, src, nread);
140+
impl<R: Reader> Reader for BufferedReader<R> {
141+
fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
142+
let nread = {
143+
let available = self.fill_buffer();
144+
if available.len() == 0 {
145+
return None;
146+
}
147+
let nread = num::min(available.len(), buf.len());
148+
vec::bytes::copy_memory(buf, available, nread);
149+
nread
150+
};
104151
self.pos += nread;
105152
Some(nread)
106153
}
@@ -355,4 +402,15 @@ mod test {
355402
stream.write(buf);
356403
stream.flush();
357404
}
405+
406+
#[test]
407+
fn test_read_until() {
408+
let inner = MemReader::new(~[0, 1, 2, 1, 0]);
409+
let mut reader = BufferedReader::with_capacity(2, inner);
410+
assert_eq!(reader.read_until(0), Some(~[0]));
411+
assert_eq!(reader.read_until(2), Some(~[1, 2]));
412+
assert_eq!(reader.read_until(1), Some(~[1]));
413+
assert_eq!(reader.read_until(8), Some(~[0]));
414+
assert_eq!(reader.read_until(9), None);
415+
}
358416
}

src/libstd/rt/io/file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ impl FileInfo for Path { }
599599
/// else { fail2!("nope"); }
600600
/// }
601601
/// ```
602-
trait DirectoryInfo : FileSystemInfo {
602+
pub trait DirectoryInfo : FileSystemInfo {
603603
/// Whether the underlying implemention (be it a file path,
604604
/// or something else) is pointing at a directory in the underlying FS.
605605
/// Will return false for paths to non-existent locations or if the item is

0 commit comments

Comments
 (0)