Change:
Address FIXMEs in core::io: Unbuffered operations? Make seekable orthogonal In reader::readchars(), handle the case where eof occurs in the middle of a Unicode char In reader::readchar(), make sure it's valid to return -1 In reader::readleuint, deal with EOF Improve forwarding implementation of reader Restructure top-level stdin() and filereader() fileflags in bufferedfilewriter() Make stdin, stdout, stderr consts Figure out why stdin() is different from stdout() and stderr() Optimize membuffer::write() Implement readwholefile() in a low-level way Improve fsync::FILEressync()
In addition, io::WriterUtil has these methods but there are no corresponding read methods on io::ReaderUtil:
Unbuffered operations please!
Nominating for milestone 2 (backwards-compatible).
The proposed changes look similar to what Go offers: is the interface that wraps the basic Close method. is the interface that wraps the basic Seek method. for buffered I/O reads from r until an error or EOF and returns the data it read.
See also
closing as WONTFIX due to and specifically

Discussion:
pub enum SeekStyle { SeekSet, SeekEnd, SeekCur, } <del> // The raw underlying reader trait. All readers must implement this. </del> <ins> /// The raw underlying reader trait. All readers must implement this. </ins> pub trait Reader { // FIXME (#2004): Seekable really should be orthogonal. <ins> /// Read up to len bytes (or EOF) and put them into bytes (which /// must be at least len bytes long). Return number of bytes read. </ins> // FIXME (#2982): This should probably return an error. fn read(bytes: &[mut u8], len: uint) -> uint; <ins> /// Read a single byte, returning a negative value for EOF or read error. </ins> fn read_byte() -> int; <ins> /// Behaves like the libc function ungetc. </ins> fn unread_byte(int); <ins> /// Return whether the stream is currently at EOF position. </ins> fn eof() -> bool; <del> fn seek(int, SeekStyle); </del> <ins> /// Move the current position within the stream. The second parameter /// determines the position that the first parameter is relative to. fn seek(position: int, style: SeekStyle); /// Return the current position within the stream. </ins> fn tell() -> uint; } <del> // Generic utility functions defined on readers </del> <ins> /// Generic utility functions defined on readers. </ins> pub trait ReaderUtil { <ins> /// Read len bytes into a new vec. </ins> fn read_bytes(len: uint) -> ~[u8]; <ins> /// Read up until the first 'n' char (which is not returned), or EOF. </ins> fn read_line() -> ~str; <ins> /// Read n utf-8 encoded chars. </ins> fn read_chars(n: uint) -> ~[char]; <ins> /// Read a single utf-8 encoded char. </ins> fn read_char() -> char; <ins> /// Read up until the first null byte (which is not returned), or EOF. </ins> fn read_c_str() -> ~str; <del> fn read_le_uint(size: uint) -> uint; fn read_le_int(size: uint) -> int; fn read_be_uint(size: uint) -> uint; </del> <ins> /// Read all the data remaining in the stream in one go. </ins> fn read_whole_stream() -> ~[u8]; <ins> /// Iterate over every byte until the iterator breaks or EOF. </ins> fn each_byte(it: fn(int) -> bool); <ins> /// Iterate over every char until the iterator breaks or EOF. </ins> fn each_char(it: fn(char) -> bool); <del> fn each_line(it: fn((&str)) -> bool); </del> <ins> /// Iterate over every line until the iterator breaks or EOF. fn each_line(it: fn(&str) -> bool); /// Read n (between 1 and 8) little-endian unsigned integer bytes. fn read_le_uint_n(nbytes: uint) -> u64; /// Read n (between 1 and 8) little-endian signed integer bytes. fn read_le_int_n(nbytes: uint) -> i64; /// Read n (between 1 and 8) big-endian unsigned integer bytes. fn read_be_uint_n(nbytes: uint) -> u64; /// Read n (between 1 and 8) big-endian signed integer bytes. fn read_be_int_n(nbytes: uint) -> i64; /// Read a little-endian uint (number of bytes depends on system). fn read_le_uint() -> uint; /// Read a little-endian int (number of bytes depends on system). fn read_le_int() -> int; /// Read a big-endian uint (number of bytes depends on system). fn read_be_uint() -> uint; /// Read a big-endian int (number of bytes depends on system). fn read_be_int() -> int; /// Read a big-endian u64 (8 bytes). fn read_be_u64() -> u64; /// Read a big-endian u32 (4 bytes). fn read_be_u32() -> u32; /// Read a big-endian u16 (2 bytes). fn read_be_u16() -> u16; /// Read a big-endian i64 (8 bytes). fn read_be_i64() -> i64; /// Read a big-endian i32 (4 bytes). fn read_be_i32() -> i32; /// Read a big-endian i16 (2 bytes). fn read_be_i16() -> i16; /// Read a little-endian u64 (8 bytes). fn read_le_u64() -> u64; /// Read a little-endian u32 (4 bytes). fn read_le_u32() -> u32; /// Read a little-endian u16 (2 bytes). fn read_le_u16() -> u16; /// Read a litle-endian i64 (8 bytes). fn read_le_i64() -> i64; /// Read a litle-endian i32 (4 bytes). fn read_le_i32() -> i32; /// Read a litle-endian i16 (2 bytes). fn read_le_i16() -> i16; /// Read a u8 (1 byte). fn read_u8() -> u8; /// Read a i8 (1 byte). fn read_i8() -> i8; </ins> } impl<T: Reader> T : ReaderUtil { fn read_bytes(len: uint) -> ~[u8] { let mut bytes = vec::with_capacity(len); unsafe { vec::raw::set_len(&mut bytes, len); }

unsafe { vec::raw::set_len(&mut bytes, count); } move bytes } fn read_line() -> ~str { let mut bytes = ~[]; loop {

str::from_bytes(bytes) } <del> // FIXME deal with eof? // #2004 fn read_le_uint(size: uint) -> uint { let mut val = 0u, pos = 0u, i = size; while i > 0u { val += (self.read_byte() as uint) << pos; pos += 8u; i -= 1u; } val } fn read_le_int(size: uint) -> int { let mut val = 0u, pos = 0u, i = size; while i > 0u { val += (self.read_byte() as uint) << pos; pos += 8u; i -= 1u; } val as int } fn read_be_uint(size: uint) -> uint { let mut val = 0u, i = size; while i > 0u { i -= 1u; val += (self.read_byte() as uint) << i * 8u; } val } </del> fn read_whole_stream() -> ~[u8] { let mut bytes: ~[u8] = ~[]; while !self.eof() { bytes.push_all(self.read_bytes(2048u)); }

if !it(self.read_line()) { break; } } } <ins> // FIXME int reading methods need to deal with eof - issue #2004 fn read_le_uint_n(nbytes: uint) -> u64 { assert nbytes > 0 && nbytes <= 8; let mut val = 0u64, pos = 0, i = nbytes; while i > 0 { val += (self.read_u8() as u64) << pos; pos += 8; i -= 1; } val } fn read_le_int_n(nbytes: uint) -> i64 { extend_sign(self.read_le_uint_n(nbytes), nbytes) } fn read_be_uint_n(nbytes: uint) -> u64 { assert nbytes > 0 && nbytes <= 8; let mut val = 0u64, i = nbytes; while i > 0 { i -= 1; val += (self.read_u8() as u64) << i * 8; } val } fn read_be_int_n(nbytes: uint) -> i64 { extend_sign(self.read_be_uint_n(nbytes), nbytes) } fn read_le_uint() -> uint { self.read_le_uint_n(uint::bytes) as uint } fn read_le_int() -> int { self.read_le_int_n(int::bytes) as int } fn read_be_uint() -> uint { self.read_be_uint_n(uint::bytes) as uint } fn read_be_int() -> int { self.read_be_int_n(int::bytes) as int } fn read_be_u64() -> u64 { self.read_be_uint_n(8) as u64 } fn read_be_u32() -> u32 { self.read_be_uint_n(4) as u32 } fn read_be_u16() -> u16 { self.read_be_uint_n(2) as u16 } fn read_be_i64() -> i64 { self.read_be_int_n(8) as i64 } fn read_be_i32() -> i32 { self.read_be_int_n(4) as i32 } fn read_be_i16() -> i16 { self.read_be_int_n(2) as i16 } fn read_le_u64() -> u64 { self.read_le_uint_n(8) as u64 } fn read_le_u32() -> u32 { self.read_le_uint_n(4) as u32 } fn read_le_u16() -> u16 { self.read_le_uint_n(2) as u16 } fn read_le_i64() -> i64 { self.read_le_int_n(8) as i64 } fn read_le_i32() -> i32 { self.read_le_int_n(4) as i32 } fn read_le_i16() -> i16 { self.read_le_int_n(2) as i16 } fn read_u8() -> u8 { self.read_byte() as u8 } fn read_i8() -> i8 { self.read_byte() as i8 } } fn extend_sign(val: u64, nbytes: uint) -> i64 { let shift = (8 - nbytes) * 8; (val << shift) as i64 >> shift </ins> } // Reader implementations

// FIXME (#2004): Seekable really should be orthogonal. // FIXME (#2004): eventually u64 <ins> /// The raw underlying writer trait. All writers must implement this. </ins> pub trait Writer { <ins> /// Write all of the given bytes. </ins> fn write(v: &[const u8]); <ins> /// Move the current position within the stream. The second parameter /// determines the position that the first parameter is relative to. </ins> fn seek(int, SeekStyle); <ins> /// Return the current position within the stream. </ins> fn tell() -> uint; <ins> /// Flush the output buffer for this stream (if there is one). </ins> fn flush() -> int; <ins> /// Determine if this Writer is writing to a file or not. </ins> fn get_type() -> WriterType; }

// FIXME: #3048 combine trait+impl (or just move these to // default methods on writer) <ins> /// Generic utility functions defined on writers. </ins> pub trait WriterUtil { <ins> /// Write a single utf-8 encoded char. </ins> fn write_char(ch: char); <ins> /// Write every char in the given str, encoded as utf-8. </ins> fn write_str(s: &str); <ins> /// Write the given str, as utf-8, followed by 'n'. </ins> fn write_line(s: &str); <ins> /// Write the result of passing n through `int::to_str_bytes`. </ins> fn write_int(n: int); <ins> /// Write the result of passing n through `uint::to_str_bytes`. </ins> fn write_uint(n: uint); <ins> /// Write a little-endian uint (number of bytes depends on system). </ins> fn write_le_uint(n: uint); <ins> /// Write a little-endian int (number of bytes depends on system). </ins> fn write_le_int(n: int); <ins> /// Write a big-endian uint (number of bytes depends on system). </ins> fn write_be_uint(n: uint); <ins> /// Write a big-endian int (number of bytes depends on system). </ins> fn write_be_int(n: int); <ins> /// Write a big-endian u64 (8 bytes). </ins> fn write_be_u64(n: u64); <ins> /// Write a big-endian u32 (4 bytes). </ins> fn write_be_u32(n: u32); <ins> /// Write a big-endian u16 (2 bytes). </ins> fn write_be_u16(n: u16); <ins> /// Write a big-endian i64 (8 bytes). </ins> fn write_be_i64(n: i64); <ins> /// Write a big-endian i32 (4 bytes). </ins> fn write_be_i32(n: i32); <ins> /// Write a big-endian i16 (2 bytes). </ins> fn write_be_i16(n: i16); <ins> /// Write a little-endian u64 (8 bytes). </ins> fn write_le_u64(n: u64); <ins> /// Write a little-endian u32 (4 bytes). </ins> fn write_le_u32(n: u32); <ins> /// Write a little-endian u16 (2 bytes). </ins> fn write_le_u16(n: u16); <ins> /// Write a little-endian i64 (8 bytes). </ins> fn write_le_i64(n: i64); <ins> /// Write a little-endian i32 (4 bytes). </ins> fn write_le_i32(n: i32); <ins> /// Write a little-endian i16 (2 bytes). </ins> fn write_le_i16(n: i16); <ins> /// Write a u8 (1 byte). </ins> fn write_u8(n: u8); <ins> /// Write a i8 (1 byte). fn write_i8(n: i8); </ins> } impl<T: Writer> T : WriterUtil {

u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) } <del> fn write_u8(n: u8) { self.write(&[n]) } </del> <ins> fn write_u8(n: u8) { self.write([n]) } fn write_i8(n: i8) { self.write([n as u8]) } </ins> } #[allow(non_implicitly_copyable_typarams)]

assert wr.bytes.borrow(|bytes| bytes == ~[0u8, 9u8, 4u8, 5u8, 8u8, 7u8]); } <ins> #[test] fn test_read_write_le() { let path = Path("tmp/lib-io-test-read-write-le.tmp"); let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; // write the ints to the file { let file = io::file_writer(&path, [io::Create]).get(); for uints.each |i| { file.write_le_u64(*i); } } // then read them back and check that they are the same { let file = io::file_reader(&path).get(); for uints.each |i| { assert file.read_le_u64() == *i; } } } #[test] fn test_read_write_be() { let path = Path("tmp/lib-io-test-read-write-be.tmp"); let uints = [0, 1, 2, 42, 10_123, 100_123_456, u64::max_value]; // write the ints to the file { let file = io::file_writer(&path, [io::Create]).get(); for uints.each |i| { file.write_be_u64(*i); } } // then read them back and check that they are the same { let file = io::file_reader(&path).get(); for uints.each |i| { assert file.read_be_u64() == *i; } } } #[test] fn test_read_be_int_n() { let path = Path("tmp/lib-io-test-read-be-int-n.tmp"); let ints = [i32::min_value, -123456, -42, -5, 0, 1, i32::max_value]; // write the ints to the file { let file = io::file_writer(&path, [io::Create]).get(); for ints.each |i| { file.write_be_i32(*i); } } // then read them back and check that they are the same { let file = io::file_reader(&path).get(); for ints.each |i| { // this tests that the sign extension is working // (comparing the values as i32 would not test this) assert file.read_be_int_n(4) == *i as i64; } } } </ins> } //