/*! Basic input/output */ #[forbid(deprecated_mode)]; #[forbid(deprecated_pattern)]; use result::Result; use cmp::Eq; use dvec::DVec; use libc::{c_int, c_long, c_uint, c_void, size_t, ssize_t}; use libc::consts::os::posix88::*; use libc::consts::os::extra::*; #[allow(non_camel_case_types)] // not sure what to do about this type fd_t = c_int; #[abi = "cdecl"] extern mod rustrt { fn rust_get_stdin() -> *libc::FILE; fn rust_get_stdout() -> *libc::FILE; fn rust_get_stderr() -> *libc::FILE; } // Reading // FIXME (#2004): This is all buffered. We might need an unbuffered variant // as well pub enum SeekStyle { SeekSet, SeekEnd, SeekCur, } // The raw underlying reader trait. All readers must implement this. pub trait Reader { // FIXME (#2004): Seekable really should be orthogonal. // FIXME (#2982): This should probably return an error. fn read(bytes: &[mut u8], len: uint) -> uint; fn read_byte() -> int; fn unread_byte(int); fn eof() -> bool; fn seek(int, SeekStyle); fn tell() -> uint; } // Generic utility functions defined on readers pub trait ReaderUtil { fn read_bytes(len: uint) -> ~[u8]; fn read_line() -> ~str; fn read_chars(n: uint) -> ~[char]; fn read_char() -> char; fn read_c_str() -> ~str; fn read_whole_stream() -> ~[u8]; fn each_byte(it: fn(int) -> bool); fn each_char(it: fn(char) -> bool); 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 read depends on system) fn read_le_uint() -> uint; /// read a little-endian int (number of bytes read depends on system) fn read_le_int() -> int; /// read a big-endian uint (number of bytes read depends on system) fn read_be_uint() -> uint; /// read a big-endian int (number of bytes read 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; } impl T : ReaderUtil { fn read_bytes(len: uint) -> ~[u8] { let mut bytes = vec::with_capacity(len); unsafe { vec::raw::set_len(&mut bytes, len); } let count = self.read(bytes, len); unsafe { vec::raw::set_len(&mut bytes, count); } move bytes } fn read_line() -> ~str { let mut bytes = ~[]; loop { let ch = self.read_byte(); if ch == -1 || ch == 10 { break; } bytes.push(ch as u8); } str::from_bytes(bytes) } fn read_chars(n: uint) -> ~[char] { // returns the (consumed offset, n_req), appends characters to &chars fn chars_from_bytes(bytes: &~[u8], chars: &mut ~[char]) -> (uint, uint) { let mut i = 0; let bytes_len = bytes.len(); while i < bytes_len { let b0 = bytes[i]; let w = str::utf8_char_width(b0); let end = i + w; i += 1; assert (w > 0); if w == 1 { chars.push(b0 as char); loop; } // can't satisfy this char with the existing data if end > bytes_len { return (i - 1, end - bytes_len); } let mut val = 0; while i < end { let next = bytes[i] as int; i += 1; assert (next > -1); assert (next & 192 == 128); val <<= 6; val += (next & 63) as uint; } // See str::char_at val += ((b0 << ((w + 1) as u8)) as uint) << (w - 1) * 6 - w - 1u; chars.push(val as char); } return (i, 0); } let mut bytes = ~[]; let mut chars = ~[]; // might need more bytes, but reading n will never over-read let mut nbread = n; while nbread > 0 { let data = self.read_bytes(nbread); if data.is_empty() { // eof - FIXME (#2004): should we do something if // we're split in a unicode char? break; } bytes.push_all(data); let (offset, nbreq) = chars_from_bytes::(&bytes, &mut chars); let ncreq = n - chars.len(); // again we either know we need a certain number of bytes // to complete a character, or we make sure we don't // over-read by reading 1-byte per char needed nbread = if ncreq > nbreq { ncreq } else { nbreq }; if nbread > 0 { bytes = vec::slice(bytes, offset, bytes.len()); } } move chars } fn read_char() -> char { let c = self.read_chars(1); if vec::len(c) == 0 { return -1 as char; // FIXME will this stay valid? // #2004 } assert(vec::len(c) == 1); return c[0]; } fn read_c_str() -> ~str { let mut bytes: ~[u8] = ~[]; loop { let ch = self.read_byte(); if ch < 1 { break; } else { bytes.push(ch as u8); } } str::from_bytes(bytes) } fn read_whole_stream() -> ~[u8] { let mut bytes: ~[u8] = ~[]; while !self.eof() { bytes.push_all(self.read_bytes(2048u)); } move bytes } fn each_byte(it: fn(int) -> bool) { while !self.eof() { if !it(self.read_byte()) { break; } } } fn each_char(it: fn(char) -> bool) { while !self.eof() { if !it(self.read_char()) { break; } } } fn each_line(it: fn(s: &str) -> bool) { while !self.eof() { if !it(self.read_line()) { break; } } } // 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 } // Reader implementations fn convert_whence(whence: SeekStyle) -> i32 { return match whence { SeekSet => 0i32, SeekCur => 1i32, SeekEnd => 2i32 }; } impl *libc::FILE: Reader { fn read(bytes: &[mut u8], len: uint) -> uint { do vec::as_mut_buf(bytes) |buf_p, buf_len| { assert buf_len <= len; let count = libc::fread(buf_p as *mut c_void, 1u as size_t, len as size_t, self); count as uint } } fn read_byte() -> int { return libc::fgetc(self) as int; } fn unread_byte(byte: int) { libc::ungetc(byte as c_int, self); } fn eof() -> bool { return libc::feof(self) != 0 as c_int; } fn seek(offset: int, whence: SeekStyle) { assert libc::fseek(self, offset as c_long, convert_whence(whence)) == 0 as c_int; } fn tell() -> uint { return libc::ftell(self) as uint; } } // A forwarding impl of reader that also holds on to a resource for the // duration of its lifetime. // FIXME there really should be a better way to do this // #2004 impl {base: T, cleanup: C}: Reader { fn read(bytes: &[mut u8], len: uint) -> uint { self.base.read(bytes, len) } fn read_byte() -> int { self.base.read_byte() } fn unread_byte(byte: int) { self.base.unread_byte(byte); } fn eof() -> bool { self.base.eof() } fn seek(off: int, whence: SeekStyle) { self.base.seek(off, whence) } fn tell() -> uint { self.base.tell() } } struct FILERes { f: *libc::FILE, drop { libc::fclose(self.f); } } fn FILERes(f: *libc::FILE) -> FILERes { FILERes { f: f } } pub fn FILE_reader(f: *libc::FILE, cleanup: bool) -> Reader { if cleanup { {base: f, cleanup: FILERes(f)} as Reader } else { f as Reader } } // FIXME (#2004): this should either be an trait-less impl, a set of // top-level functions that take a reader, or a set of default methods on // reader (which can then be called reader) pub fn stdin() -> Reader { rustrt::rust_get_stdin() as Reader } pub fn file_reader(path: &Path) -> Result { let f = os::as_c_charp(path.to_str(), |pathbuf| { os::as_c_charp("r", |modebuf| libc::fopen(pathbuf, modebuf) ) }); return if f as uint == 0u { result::Err(~"error opening " + path.to_str()) } else { result::Ok(FILE_reader(f, true)) } } // Byte readers pub struct BytesReader { bytes: &[u8], mut pos: uint } impl BytesReader: Reader { fn read(bytes: &[mut u8], len: uint) -> uint { let count = uint::min(len, self.bytes.len() - self.pos); let view = vec::view(self.bytes, self.pos, self.bytes.len()); vec::bytes::memcpy(bytes, view, count); self.pos += count; count } fn read_byte() -> int { if self.pos == self.bytes.len() { return -1; } let b = self.bytes[self.pos]; self.pos += 1u; return b as int; } // FIXME (#2738): implement this fn unread_byte(_byte: int) { error!("Unimplemented: unread_byte"); fail; } fn eof() -> bool { self.pos == self.bytes.len() } fn seek(offset: int, whence: SeekStyle) { let pos = self.pos; self.pos = seek_in_buf(offset, pos, self.bytes.len(), whence); } fn tell() -> uint { self.pos } } pub pure fn with_bytes_reader(bytes: &[u8], f: fn(Reader) -> t) -> t { f(BytesReader { bytes: bytes, pos: 0u } as Reader) } pub fn with_str_reader(s: &str, f: fn(Reader) -> T) -> T { str::byte_slice(s, |bytes| with_bytes_reader(bytes, f)) } // Writing pub enum FileFlag { Append, Create, Truncate, NoFlag, } // What type of writer are we? pub enum WriterType { Screen, File } pub impl WriterType : Eq { pure fn eq(other: &WriterType) -> bool { match (self, (*other)) { (Screen, Screen) | (File, File) => true, (Screen, _) | (File, _) => false } } pure fn ne(other: &WriterType) -> bool { !self.eq(other) } } // FIXME (#2004): Seekable really should be orthogonal. // FIXME (#2004): eventually u64 pub trait Writer { fn write(v: &[const u8]); fn seek(int, SeekStyle); fn tell() -> uint; fn flush() -> int; fn get_type() -> WriterType; } impl {base: T, cleanup: C}: Writer { fn write(bs: &[const u8]) { self.base.write(bs); } fn seek(off: int, style: SeekStyle) { self.base.seek(off, style); } fn tell() -> uint { self.base.tell() } fn flush() -> int { self.base.flush() } fn get_type() -> WriterType { File } } impl *libc::FILE: Writer { fn write(v: &[const u8]) { do vec::as_const_buf(v) |vbuf, len| { let nout = libc::fwrite(vbuf as *c_void, 1, len as size_t, self); if nout != len as size_t { error!("error writing buffer"); log(error, os::last_os_error()); fail; } } } fn seek(offset: int, whence: SeekStyle) { assert libc::fseek(self, offset as c_long, convert_whence(whence)) == 0 as c_int; } fn tell() -> uint { libc::ftell(self) as uint } fn flush() -> int { libc::fflush(self) as int } fn get_type() -> WriterType { let fd = libc::fileno(self); if libc::isatty(fd) == 0 { File } else { Screen } } } pub fn FILE_writer(f: *libc::FILE, cleanup: bool) -> Writer { if cleanup { {base: f, cleanup: FILERes(f)} as Writer } else { f as Writer } } impl fd_t: Writer { fn write(v: &[const u8]) { let mut count = 0u; do vec::as_const_buf(v) |vbuf, len| { while count < len { let vb = ptr::const_offset(vbuf, count) as *c_void; let nout = libc::write(self, vb, len as size_t); if nout < 0 as ssize_t { error!("error writing buffer"); log(error, os::last_os_error()); fail; } count += nout as uint; } } } fn seek(_offset: int, _whence: SeekStyle) { error!("need 64-bit foreign calls for seek, sorry"); fail; } fn tell() -> uint { error!("need 64-bit foreign calls for tell, sorry"); fail; } fn flush() -> int { 0 } fn get_type() -> WriterType { if libc::isatty(self) == 0 { File } else { Screen } } } struct FdRes { fd: fd_t, drop { libc::close(self.fd); } } fn FdRes(fd: fd_t) -> FdRes { FdRes { fd: fd } } pub fn fd_writer(fd: fd_t, cleanup: bool) -> Writer { if cleanup { {base: fd, cleanup: FdRes(fd)} as Writer } else { fd as Writer } } pub fn mk_file_writer(path: &Path, flags: &[FileFlag]) -> Result { #[cfg(windows)] fn wb() -> c_int { (O_WRONLY | O_BINARY) as c_int } #[cfg(unix)] fn wb() -> c_int { O_WRONLY as c_int } let mut fflags: c_int = wb(); for vec::each(flags) |f| { match *f { Append => fflags |= O_APPEND as c_int, Create => fflags |= O_CREAT as c_int, Truncate => fflags |= O_TRUNC as c_int, NoFlag => () } } let fd = do os::as_c_charp(path.to_str()) |pathbuf| { libc::open(pathbuf, fflags, (S_IRUSR | S_IWUSR) as c_int) }; if fd < (0 as c_int) { result::Err(fmt!("error opening %s: %s", path.to_str(), os::last_os_error())) } else { result::Ok(fd_writer(fd, true)) } } pub fn u64_to_le_bytes(n: u64, size: uint, f: fn(v: &[u8]) -> T) -> T { assert size <= 8u; match size { 1u => f(&[n as u8]), 2u => f(&[n as u8, (n >> 8) as u8]), 4u => f(&[n as u8, (n >> 8) as u8, (n >> 16) as u8, (n >> 24) as u8]), 8u => f(&[n as u8, (n >> 8) as u8, (n >> 16) as u8, (n >> 24) as u8, (n >> 32) as u8, (n >> 40) as u8, (n >> 48) as u8, (n >> 56) as u8]), _ => { let mut bytes: ~[u8] = ~[], i = size, n = n; while i > 0u { bytes.push((n & 255_u64) as u8); n >>= 8_u64; i -= 1u; } f(bytes) } } } pub fn u64_to_be_bytes(n: u64, size: uint, f: fn(v: &[u8]) -> T) -> T { assert size <= 8u; match size { 1u => f(&[n as u8]), 2u => f(&[(n >> 8) as u8, n as u8]), 4u => f(&[(n >> 24) as u8, (n >> 16) as u8, (n >> 8) as u8, n as u8]), 8u => f(&[(n >> 56) as u8, (n >> 48) as u8, (n >> 40) as u8, (n >> 32) as u8, (n >> 24) as u8, (n >> 16) as u8, (n >> 8) as u8, n as u8]), _ => { let mut bytes: ~[u8] = ~[]; let mut i = size; while i > 0u { let shift = ((i - 1u) * 8u) as u64; bytes.push((n >> shift) as u8); i -= 1u; } f(bytes) } } } pub fn u64_from_be_bytes(data: &[const u8], start: uint, size: uint) -> u64 { let mut sz = size; assert (sz <= 8u); let mut val = 0_u64; let mut pos = start; while sz > 0u { sz -= 1u; val += (data[pos] as u64) << ((sz * 8u) as u64); pos += 1u; } return val; } // FIXME: #3048 combine trait+impl (or just move these to // default methods on writer) pub trait WriterUtil { fn write_char(ch: char); fn write_str(s: &str); fn write_line(s: &str); fn write_int(n: int); fn write_uint(n: uint); fn write_le_uint(n: uint); fn write_le_int(n: int); fn write_be_uint(n: uint); fn write_be_int(n: int); fn write_be_u64(n: u64); fn write_be_u32(n: u32); fn write_be_u16(n: u16); fn write_be_i64(n: i64); fn write_be_i32(n: i32); fn write_be_i16(n: i16); fn write_le_u64(n: u64); fn write_le_u32(n: u32); fn write_le_u16(n: u16); fn write_le_i64(n: i64); fn write_le_i32(n: i32); fn write_le_i16(n: i16); fn write_u8(n: u8); fn write_i8(n: i8); } impl T : WriterUtil { fn write_char(ch: char) { if ch as uint < 128u { self.write(&[ch as u8]); } else { self.write_str(str::from_char(ch)); } } fn write_str(s: &str) { str::byte_slice(s, |v| self.write(v)) } fn write_line(s: &str) { self.write_str(s); self.write_str(&"\n"); } fn write_int(n: int) { int::to_str_bytes(n, 10u, |bytes| self.write(bytes)) } fn write_uint(n: uint) { uint::to_str_bytes(false, n, 10u, |bytes| self.write(bytes)) } fn write_le_uint(n: uint) { u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v)) } fn write_le_int(n: int) { u64_to_le_bytes(n as u64, int::bytes, |v| self.write(v)) } fn write_be_uint(n: uint) { u64_to_be_bytes(n as u64, uint::bytes, |v| self.write(v)) } fn write_be_int(n: int) { u64_to_be_bytes(n as u64, int::bytes, |v| self.write(v)) } fn write_be_u64(n: u64) { u64_to_be_bytes(n, 8u, |v| self.write(v)) } fn write_be_u32(n: u32) { u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) } fn write_be_u16(n: u16) { u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) } fn write_be_i64(n: i64) { u64_to_be_bytes(n as u64, 8u, |v| self.write(v)) } fn write_be_i32(n: i32) { u64_to_be_bytes(n as u64, 4u, |v| self.write(v)) } fn write_be_i16(n: i16) { u64_to_be_bytes(n as u64, 2u, |v| self.write(v)) } fn write_le_u64(n: u64) { u64_to_le_bytes(n, 8u, |v| self.write(v)) } fn write_le_u32(n: u32) { u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) } fn write_le_u16(n: u16) { u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) } fn write_le_i64(n: i64) { u64_to_le_bytes(n as u64, 8u, |v| self.write(v)) } fn write_le_i32(n: i32) { u64_to_le_bytes(n as u64, 4u, |v| self.write(v)) } fn write_le_i16(n: i16) { u64_to_le_bytes(n as u64, 2u, |v| self.write(v)) } fn write_u8(n: u8) { self.write([n]) } fn write_i8(n: i8) { self.write([n as u8]) } } #[allow(non_implicitly_copyable_typarams)] pub fn file_writer(path: &Path, flags: &[FileFlag]) -> Result { mk_file_writer(path, flags).chain(|w| result::Ok(w)) } // FIXME: fileflags // #2004 pub fn buffered_file_writer(path: &Path) -> Result { let f = do os::as_c_charp(path.to_str()) |pathbuf| { do os::as_c_charp("w") |modebuf| { libc::fopen(pathbuf, modebuf) } }; return if f as uint == 0u { result::Err(~"error opening " + path.to_str()) } else { result::Ok(FILE_writer(f, true)) } } // FIXME (#2004) it would be great if this could be a const // FIXME (#2004) why are these different from the way stdin() is // implemented? pub fn stdout() -> Writer { fd_writer(libc::STDOUT_FILENO as c_int, false) } pub fn stderr() -> Writer { fd_writer(libc::STDERR_FILENO as c_int, false) } pub fn print(s: &str) { stdout().write_str(s); } pub fn println(s: &str) { stdout().write_line(s); } pub struct BytesWriter { bytes: DVec, mut pos: uint, } impl BytesWriter: Writer { fn write(v: &[const u8]) { do self.bytes.swap |bytes| { let mut bytes = move bytes; let v_len = v.len(); let bytes_len = bytes.len(); let count = uint::max(bytes_len, self.pos + v_len); vec::reserve(&mut bytes, count); unsafe { vec::raw::set_len(&mut bytes, count); } { let view = vec::mut_view(bytes, self.pos, count); vec::bytes::memcpy(view, v, v_len); } self.pos += v_len; move bytes } } fn seek(offset: int, whence: SeekStyle) { let pos = self.pos; let len = self.bytes.len(); self.pos = seek_in_buf(offset, pos, len, whence); } fn tell() -> uint { self.pos } fn flush() -> int { 0 } fn get_type() -> WriterType { File } } impl @BytesWriter : Writer { fn write(v: &[const u8]) { (*self).write(v) } fn seek(offset: int, whence: SeekStyle) { (*self).seek(offset, whence) } fn tell() -> uint { (*self).tell() } fn flush() -> int { (*self).flush() } fn get_type() -> WriterType { (*self).get_type() } } pub pure fn BytesWriter() -> BytesWriter { BytesWriter { bytes: DVec(), mut pos: 0u } } pub pure fn with_bytes_writer(f: fn(Writer)) -> ~[u8] { let wr = @BytesWriter(); f(wr as Writer); // FIXME (#3758): This should not be needed. unsafe { wr.bytes.check_out(|bytes| move bytes) } } pub pure fn with_str_writer(f: fn(Writer)) -> ~str { let mut v = with_bytes_writer(f); // FIXME (#3758): This should not be needed. unsafe { // Make sure the vector has a trailing null and is proper utf8. v.push(0); } assert str::is_utf8(v); unsafe { move ::cast::transmute(move v) } } // Utility functions pub fn seek_in_buf(offset: int, pos: uint, len: uint, whence: SeekStyle) -> uint { let mut bpos = pos as int; let blen = len as int; match whence { SeekSet => bpos = offset, SeekCur => bpos += offset, SeekEnd => bpos = blen + offset } if bpos < 0 { bpos = 0; } else if bpos > blen { bpos = blen; } return bpos as uint; } #[allow(non_implicitly_copyable_typarams)] pub fn read_whole_file_str(file: &Path) -> Result<~str, ~str> { result::chain(read_whole_file(file), |bytes| { if str::is_utf8(bytes) { result::Ok(str::from_bytes(bytes)) } else { result::Err(file.to_str() + ~" is not UTF-8") } }) } // FIXME (#2004): implement this in a low-level way. Going through the // abstractions is pointless. #[allow(non_implicitly_copyable_typarams)] pub fn read_whole_file(file: &Path) -> Result<~[u8], ~str> { result::chain(file_reader(file), |rdr| { result::Ok(rdr.read_whole_stream()) }) } // fsync related pub mod fsync { pub enum Level { // whatever fsync does on that platform FSync, // fdatasync on linux, similiar or more on other platforms FDataSync, // full fsync // // You must additionally sync the parent directory as well! FullFSync, } // Artifacts that need to fsync on destruction pub struct Res { arg: Arg, drop { match self.arg.opt_level { option::None => (), option::Some(level) => { // fail hard if not succesful assert(self.arg.fsync_fn(self.arg.val, level) != -1); } } } } pub fn Res(arg: Arg) -> Res{ Res { arg: move arg } } pub type Arg = { val: t, opt_level: Option, fsync_fn: fn@(f: t, Level) -> int }; // fsync file after executing blk // FIXME (#2004) find better way to create resources within lifetime of // outer res pub fn FILE_res_sync(file: &FILERes, opt_level: Option, blk: fn(v: Res<*libc::FILE>)) { blk(move Res({ val: file.f, opt_level: opt_level, fsync_fn: fn@(file: *libc::FILE, l: Level) -> int { return os::fsync_fd(libc::fileno(file), l) as int; } })); } // fsync fd after executing blk pub fn fd_res_sync(fd: &FdRes, opt_level: Option, blk: fn(v: Res)) { blk(move Res({ val: fd.fd, opt_level: opt_level, fsync_fn: fn@(fd: fd_t, l: Level) -> int { return os::fsync_fd(fd, l) as int; } })); } // Type of objects that may want to fsync pub trait FSyncable { fn fsync(l: Level) -> int; } // Call o.fsync after executing blk pub fn obj_sync(o: FSyncable, opt_level: Option, blk: fn(v: Res)) { blk(Res({ val: o, opt_level: opt_level, fsync_fn: fn@(o: FSyncable, l: Level) -> int { return o.fsync(l); } })); } } #[cfg(test)] mod tests { #[test] fn test_simple() { let tmpfile = &Path("tmp/lib-io-test-simple.tmp"); log(debug, tmpfile); let frood: ~str = ~"A hoopy frood who really knows where his towel is."; log(debug, frood); { let out: io::Writer = result::get( &io::file_writer(tmpfile, ~[io::Create, io::Truncate])); out.write_str(frood); } let inp: io::Reader = result::get(&io::file_reader(tmpfile)); let frood2: ~str = inp.read_c_str(); log(debug, frood2); assert frood == frood2; } #[test] fn test_readchars_empty() { do io::with_str_reader(~"") |inp| { let res : ~[char] = inp.read_chars(128); assert(vec::len(res) == 0); } } #[test] fn test_readchars_wide() { let wide_test = ~"生锈的汤匙切肉汤hello生锈的汤匙切肉汤"; let ivals : ~[int] = ~[ 29983, 38152, 30340, 27748, 21273, 20999, 32905, 27748, 104, 101, 108, 108, 111, 29983, 38152, 30340, 27748, 21273, 20999, 32905, 27748]; fn check_read_ln(len : uint, s: &str, ivals: &[int]) { do io::with_str_reader(s) |inp| { let res : ~[char] = inp.read_chars(len); if (len <= vec::len(ivals)) { assert(vec::len(res) == len); } assert(vec::slice(ivals, 0u, vec::len(res)) == vec::map(res, |x| *x as int)); } } let mut i = 0; while i < 8 { check_read_ln(i, wide_test, ivals); i += 1; } // check a long read for good measure check_read_ln(128, wide_test, ivals); } #[test] fn test_readchar() { do io::with_str_reader(~"生") |inp| { let res : char = inp.read_char(); assert(res as int == 29983); } } #[test] fn test_readchar_empty() { do io::with_str_reader(~"") |inp| { let res : char = inp.read_char(); assert(res as int == -1); } } #[test] fn file_reader_not_exist() { match io::file_reader(&Path("not a file")) { result::Err(copy e) => { assert e == ~"error opening not a file"; } result::Ok(_) => fail } } #[test] fn test_write_empty() { let file = io::file_writer(&Path("tmp/lib-io-test-write-empty.tmp"), [io::Create]).get(); file.write([]); } #[test] fn file_writer_bad_name() { match io::file_writer(&Path("?/?"), ~[]) { result::Err(copy e) => { assert str::starts_with(e, "error opening"); } result::Ok(_) => fail } } #[test] fn buffered_file_writer_bad_name() { match io::buffered_file_writer(&Path("?/?")) { result::Err(copy e) => { assert str::starts_with(e, "error opening"); } result::Ok(_) => fail } } #[test] fn bytes_buffer_overwrite() { let wr = BytesWriter(); wr.write(~[0u8, 1u8, 2u8, 3u8]); assert wr.bytes.borrow(|bytes| bytes == ~[0u8, 1u8, 2u8, 3u8]); wr.seek(-2, SeekCur); wr.write(~[4u8, 5u8, 6u8, 7u8]); assert wr.bytes.borrow(|bytes| bytes == ~[0u8, 1u8, 4u8, 5u8, 6u8, 7u8]); wr.seek(-2, SeekEnd); wr.write(~[8u8]); wr.seek(1, SeekSet); wr.write(~[9u8]); assert wr.bytes.borrow(|bytes| bytes == ~[0u8, 9u8, 4u8, 5u8, 8u8, 7u8]); } #[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; } } } } // // Local Variables: // mode: rust // fill-column: 78; // indent-tabs-mode: nil // c-basic-offset: 4 // buffer-file-coding-system: utf-8-unix // End: //