// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

The `io` module contains basic input and output routines.

A quick summary:

## `Reader` and `Writer` traits

These traits define the minimal set of methods that anything that can do
input and output should implement.

## `ReaderUtil` and `WriterUtil` traits

Richer methods that allow you to do more. `Reader` only lets you read a certain
number of bytes into a buffer, while `ReaderUtil` allows you to read a whole
line, for example.

Generally, these richer methods are probably the ones you want to actually
use in day-to-day Rust.

Furthermore, because there is an implementation of `ReaderUtil` for
`<T: Reader>`, when your input or output code implements `Reader`, you get
all of these methods for free.

## `print` and `println`

These very useful functions are defined here. You generally don't need to
import them, though, as the prelude already does.

## `stdin`, `stdout`, and `stderr`

These functions return references to the classic three file descriptors. They
implement `Reader` and `Writer`, where appropriate.

*/

#[allow(missing_doc)];

use cast;
use clone::Clone;
use c_str::ToCStr;
use container::Container;
use int;
use iterator::Iterator;
use libc::consts::os::posix88::*;
use libc::{c_int, c_long, c_void, size_t, ssize_t};
use libc;
use num;
use ops::Drop;
use option::{Some, None};
use os;
use path::Path;
use ptr;
use result::{Result, Ok, Err};
use str::{StrSlice, OwnedStr};
use str;
use to_str::ToStr;
use uint;
use vec::{MutableVector, ImmutableVector, OwnedVector, OwnedCopyableVector, CopyableVector};
use vec;

#[allow(non_camel_case_types)] // not sure what to do about this
pub type fd_t = c_int;

pub mod rustrt {
    use libc;

    #[abi = "cdecl"]
    #[link_name = "rustrt"]
    extern {
        pub fn rust_get_stdin() -> *libc::FILE;
        pub fn rust_get_stdout() -> *libc::FILE;
        pub fn rust_get_stderr() -> *libc::FILE;
    }
}

// Reading

// FIXME (#2004): This is all buffered. We might need an unbuffered variant
// as well
/**
* The SeekStyle enum describes the relationship between the position
* we'd like to seek to from our current position. It's used as an argument
* to the `seek` method defined on the `Reader` trait.
*
* There are three seek styles:
*
* 1. `SeekSet` means that the new position should become our position.
* 2. `SeekCur` means that we should seek from the current position.
* 3. `SeekEnd` means that we should seek from the end.
*
* # Examples
*
* None right now.
*/
pub enum SeekStyle { SeekSet, SeekEnd, SeekCur, }


/**
* The core Reader trait. All readers must implement this trait.
*
* # Examples
*
* None right now.
*/
pub trait Reader {
    // FIXME (#2004): Seekable really should be orthogonal.

    // FIXME (#2982): This should probably return an error.
    /**
    * Reads bytes and puts them into `bytes`, advancing the cursor. Returns the
    * number of bytes read.
    *
    * The number of bytes to be read is `len` or the end of the file,
    * whichever comes first.
    *
    * The buffer must be at least `len` bytes long.
    *
    * `read` is conceptually similar to C's `fread` function.
    *
    * # Examples
    *
    * None right now.
    */
    fn read(&self, bytes: &mut [u8], len: uint) -> uint;

    /**
    * Reads a single byte, advancing the cursor.
    *
    * In the case of an EOF or an error, returns a negative value.
    *
    * `read_byte` is conceptually similar to C's `getc` function.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_byte(&self) -> int;

    /**
    * Returns a boolean value: are we currently at EOF?
    *
    * Note that stream position may be already at the end-of-file point,
    * but `eof` returns false until an attempt to read at that position.
    *
    * `eof` is conceptually similar to C's `feof` function.
    *
    * # Examples
    *
    * None right now.
    */
    fn eof(&self) -> bool;

    /**
    * Seek to a given `position` in the stream.
    *
    * Takes an optional SeekStyle, which affects how we seek from the
    * position. See `SeekStyle` docs for more details.
    *
    * `seek` is conceptually similar to C's `fseek` function.
    *
    * # Examples
    *
    * None right now.
    */
    fn seek(&self, position: int, style: SeekStyle);

    /**
    * Returns the current position within the stream.
    *
    * `tell` is conceptually similar to C's `ftell` function.
    *
    * # Examples
    *
    * None right now.
    */
    fn tell(&self) -> uint;
}

impl Reader for @Reader {
    fn read(&self, bytes: &mut [u8], len: uint) -> uint {
        self.read(bytes, len)
    }
    fn read_byte(&self) -> int {
        self.read_byte()
    }
    fn eof(&self) -> bool {
        self.eof()
    }
    fn seek(&self, position: int, style: SeekStyle) {
        self.seek(position, style)
    }
    fn tell(&self) -> uint {
        self.tell()
    }
}

/**
* The `ReaderUtil` trait is a home for many of the utility functions
* a particular Reader should implement.
*
* The default `Reader` trait is focused entirely on bytes. `ReaderUtil` is based
* on higher-level concepts like 'chars' and 'lines.'
*
* # Examples:
*
* None right now.
*/
pub trait ReaderUtil {

    /**
    * Reads `len` number of bytes, and gives you a new vector back.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_bytes(&self, len: uint) -> ~[u8];

    /**
    * Reads up until a specific byte is seen or EOF.
    *
    * The `include` parameter specifies if the character should be included
    * in the returned string.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_until(&self, c: u8, include: bool) -> ~str;

    /**
    * Reads up until the first '\n' or EOF.
    *
    * The '\n' is not included in the result.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_line(&self) -> ~str;

    /**
    * Reads `n` chars.
    *
    * Assumes that those chars are UTF-8 encoded.
    *
    * The '\n' is not included in the result.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_chars(&self, n: uint) -> ~[char];

    /**
    * Reads a single UTF-8 encoded char.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_char(&self) -> char;

    /**
    * Reads up until the first null byte or EOF.
    *
    * The null byte is not returned.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_c_str(&self) -> ~str;

    /**
    * Reads all remaining data in the stream.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_whole_stream(&self) -> ~[u8];

    /**
    * Iterate over every byte until EOF or the iterator breaks.
    *
    * # Examples
    *
    * None right now.
    */
    fn each_byte(&self, it: &fn(int) -> bool) -> bool;

    /**
    * Iterate over every char until EOF or the iterator breaks.
    *
    * # Examples
    *
    * None right now.
    */
    fn each_char(&self, it: &fn(char) -> bool) -> bool;

    /**
    * Iterate over every line until EOF or the iterator breaks.
    *
    * # Examples
    *
    * None right now.
    */
    fn each_line(&self, it: &fn(&str) -> bool) -> bool;

    /**
    * Reads all of the lines in the stream.
    *
    * Returns a vector of those lines.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_lines(&self) -> ~[~str];

    /**
    * Reads `n` little-endian unsigned integer bytes.
    *
    * `n` must be between 1 and 8, inclusive.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_uint_n(&self, nbytes: uint) -> u64;

    /**
    * Reads `n` little-endian signed integer bytes.
    *
    * `n` must be between 1 and 8, inclusive.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_int_n(&self, nbytes: uint) -> i64;

    /**
    * Reads `n` big-endian unsigned integer bytes.
    *
    * `n` must be between 1 and 8, inclusive.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_uint_n(&self, nbytes: uint) -> u64;

    /**
    * Reads `n` big-endian signed integer bytes.
    *
    * `n` must be between 1 and 8, inclusive.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_int_n(&self, nbytes: uint) -> i64;

    /**
    * Reads a little-endian unsigned integer.
    *
    * The number of bytes returned is system-dependant.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_uint(&self) -> uint;

    /**
    * Reads a little-endian integer.
    *
    * The number of bytes returned is system-dependant.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_int(&self) -> int;

    /**
    * Reads a big-endian unsigned integer.
    *
    * The number of bytes returned is system-dependant.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_uint(&self) -> uint;

    /**
    * Reads a big-endian integer.
    *
    * The number of bytes returned is system-dependant.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_int(&self) -> int;

    /**
    * Reads a big-endian `u64`.
    *
    * `u64`s are 8 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_u64(&self) -> u64;

    /**
    * Reads a big-endian `u32`.
    *
    * `u32`s are 4 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_u32(&self) -> u32;

    /**
    * Reads a big-endian `u16`.
    *
    * `u16`s are 2 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_u16(&self) -> u16;

    /**
    * Reads a big-endian `i64`.
    *
    * `i64`s are 8 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_i64(&self) -> i64;

    /**
    * Reads a big-endian `i32`.
    *
    * `i32`s are 4 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_i32(&self) -> i32;

    /**
    * Reads a big-endian `i16`.
    *
    * `i16`s are 2 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_i16(&self) -> i16;

    /**
    * Reads a big-endian `f64`.
    *
    * `f64`s are 8 byte, IEEE754 double-precision floating point numbers.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_f64(&self) -> f64;

    /**
    * Reads a big-endian `f32`.
    *
    * `f32`s are 4 byte, IEEE754 single-precision floating point numbers.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_be_f32(&self) -> f32;

    /**
    * Reads a little-endian `u64`.
    *
    * `u64`s are 8 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_u64(&self) -> u64;

    /**
    * Reads a little-endian `u32`.
    *
    * `u32`s are 4 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_u32(&self) -> u32;

    /**
    * Reads a little-endian `u16`.
    *
    * `u16`s are 2 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_u16(&self) -> u16;

    /**
    * Reads a little-endian `i64`.
    *
    * `i64`s are 8 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_i64(&self) -> i64;

    /**
    * Reads a little-endian `i32`.
    *
    * `i32`s are 4 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_i32(&self) -> i32;

    /**
    * Reads a little-endian `i16`.
    *
    * `i16`s are 2 bytes long.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_i16(&self) -> i16;

    /**
    * Reads a little-endian `f64`.
    *
    * `f64`s are 8 byte, IEEE754 double-precision floating point numbers.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_f64(&self) -> f64;

    /**
    * Reads a little-endian `f32`.
    *
    * `f32`s are 4 byte, IEEE754 single-precision floating point numbers.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_le_f32(&self) -> f32;

    /**
    * Read a u8.
    *
    * `u8`s are 1 byte.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_u8(&self) -> u8;

    /**
    * Read an i8.
    *
    * `i8`s are 1 byte.
    *
    * # Examples
    *
    * None right now.
    */
    fn read_i8(&self) -> i8;
}

impl<T:Reader> ReaderUtil for T {

    fn read_bytes(&self,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); }
        bytes
    }

    fn read_until(&self, c: u8, include: bool) -> ~str {
        let mut bytes = ~[];
        loop {
            let ch = self.read_byte();
            if ch == -1 || ch == c as int {
                if include && ch == c as int {
                    bytes.push(ch as u8);
                }
                break;
            }
            bytes.push(ch as u8);
        }
        str::from_bytes(bytes)
    }

    fn read_line(&self) -> ~str {
        self.read_until('\n' as u8, false)
    }

    fn read_chars(&self, n: uint) -> ~[char] {
        // returns the (consumed offset, n_req), appends characters to &chars
        fn chars_from_bytes<T:Reader>(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_eq!(next & 192, 128);
                    val <<= 6;
                    val += (next & 63) as uint;
                }
                // See str::StrSlice::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::<T>(&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 = bytes.slice(offset, bytes.len()).to_owned();
            }
        }
        chars
    }

    fn read_char(&self) -> char {
        let c = self.read_chars(1);
        if c.len() == 0 {
            return -1 as char; // FIXME will this stay valid? // #2004
        }
        assert_eq!(c.len(), 1);
        return c[0];
    }

    fn read_c_str(&self) -> ~str {
        self.read_until(0u8, false)
    }

    fn read_whole_stream(&self) -> ~[u8] {
        let mut bytes: ~[u8] = ~[];
        while !self.eof() { bytes.push_all(self.read_bytes(2048u)); }
        bytes
    }

    fn each_byte(&self, it: &fn(int) -> bool) -> bool {
        loop {
            match self.read_byte() {
                -1 => break,
                ch => if !it(ch) { return false; }
            }
        }
        return true;
    }

    fn each_char(&self, it: &fn(char) -> bool) -> bool {
        loop {
            match self.read_char() {
                eof if eof == (-1 as char) => break,
                ch => if !it(ch) { return false; }
            }
        }
        return true;
    }

    fn each_line(&self, it: &fn(s: &str) -> bool) -> bool {
        while !self.eof() {
            // include the \n, so that we can distinguish an entirely empty
            // line read after "...\n", and the trailing empty line in
            // "...\n\n".
            let mut line = self.read_until('\n' as u8, true);

            // blank line at the end of the reader is ignored
            if self.eof() && line.is_empty() { break; }

            // trim the \n, so that each_line is consistent with read_line
            let n = line.len();
            if line[n-1] == '\n' as u8 {
                unsafe { str::raw::set_len(&mut line, n-1); }
            }

            if !it(line) { return false; }
        }
        return true;
    }

    fn read_lines(&self) -> ~[~str] {
        do vec::build |push| {
            do self.each_line |line| {
                push(line.to_owned());
                true
            };
        }
    }

    // FIXME int reading methods need to deal with eof - issue #2004

    fn read_le_uint_n(&self, nbytes: uint) -> u64 {
        assert!(nbytes > 0 && nbytes <= 8);

        let mut val = 0u64;
        let mut pos = 0;
        let mut i = nbytes;
        while i > 0 {
            val += (self.read_u8() as u64) << pos;
            pos += 8;
            i -= 1;
        }
        val
    }

    fn read_le_int_n(&self, nbytes: uint) -> i64 {
        extend_sign(self.read_le_uint_n(nbytes), nbytes)
    }

    fn read_be_uint_n(&self, nbytes: uint) -> u64 {
        assert!(nbytes > 0 && nbytes <= 8);

        let mut val = 0u64;
        let mut i = nbytes;
        while i > 0 {
            i -= 1;
            val += (self.read_u8() as u64) << i * 8;
        }
        val
    }

    fn read_be_int_n(&self, nbytes: uint) -> i64 {
        extend_sign(self.read_be_uint_n(nbytes), nbytes)
    }

    fn read_le_uint(&self) -> uint {
        self.read_le_uint_n(uint::bytes) as uint
    }

    fn read_le_int(&self) -> int {
        self.read_le_int_n(int::bytes) as int
    }

    fn read_be_uint(&self) -> uint {
        self.read_be_uint_n(uint::bytes) as uint
    }

    fn read_be_int(&self) -> int {
        self.read_be_int_n(int::bytes) as int
    }

    fn read_be_u64(&self) -> u64 {
        self.read_be_uint_n(8) as u64
    }

    fn read_be_u32(&self) -> u32 {
        self.read_be_uint_n(4) as u32
    }

    fn read_be_u16(&self) -> u16 {
        self.read_be_uint_n(2) as u16
    }

    fn read_be_i64(&self) -> i64 {
        self.read_be_int_n(8) as i64
    }

    fn read_be_i32(&self) -> i32 {
        self.read_be_int_n(4) as i32
    }

    fn read_be_i16(&self) -> i16 {
        self.read_be_int_n(2) as i16
    }

    fn read_be_f64(&self) -> f64 {
        unsafe {
            cast::transmute::<u64, f64>(self.read_be_u64())
        }
    }

    fn read_be_f32(&self) -> f32 {
        unsafe {
            cast::transmute::<u32, f32>(self.read_be_u32())
        }
    }

    fn read_le_u64(&self) -> u64 {
        self.read_le_uint_n(8) as u64
    }

    fn read_le_u32(&self) -> u32 {
        self.read_le_uint_n(4) as u32
    }

    fn read_le_u16(&self) -> u16 {
        self.read_le_uint_n(2) as u16
    }

    fn read_le_i64(&self) -> i64 {
        self.read_le_int_n(8) as i64
    }

    fn read_le_i32(&self) -> i32 {
        self.read_le_int_n(4) as i32
    }

    fn read_le_i16(&self) -> i16 {
        self.read_le_int_n(2) as i16
    }

    fn read_le_f64(&self) -> f64 {
        unsafe {
            cast::transmute::<u64, f64>(self.read_le_u64())
        }
    }

    fn read_le_f32(&self) -> f32 {
        unsafe {
            cast::transmute::<u32, f32>(self.read_le_u32())
        }
    }

    fn read_u8(&self) -> u8 {
        self.read_byte() as u8
    }

    fn read_i8(&self) -> 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 Reader for *libc::FILE {
    fn read(&self, bytes: &mut [u8], len: uint) -> uint {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            do bytes.as_mut_buf |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) as uint;
                if count < len {
                  match libc::ferror(*self) {
                    0 => (),
                    _ => {
                      error!("error reading buffer");
                      error!("%s", os::last_os_error());
                      fail!();
                    }
                  }
                }

                count
            }
        }
    }
    fn read_byte(&self) -> int {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            libc::fgetc(*self) as int
        }
    }
    fn eof(&self) -> bool {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            return libc::feof(*self) != 0 as c_int;
        }
    }
    fn seek(&self, offset: int, whence: SeekStyle) {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            assert!(libc::fseek(*self,
                                     offset as c_long,
                                     convert_whence(whence)) == 0 as c_int);
        }
    }
    fn tell(&self) -> uint {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            return libc::ftell(*self) as uint;
        }
    }
}

struct Wrapper<T, C> {
    base: T,
    cleanup: C,
}

// 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<R:Reader,C> Reader for Wrapper<R, C> {
    fn read(&self, bytes: &mut [u8], len: uint) -> uint {
        self.base.read(bytes, len)
    }
    fn read_byte(&self) -> int { self.base.read_byte() }
    fn eof(&self) -> bool { self.base.eof() }
    fn seek(&self, off: int, whence: SeekStyle) {
        self.base.seek(off, whence)
    }
    fn tell(&self) -> uint { self.base.tell() }
}

pub struct FILERes {
    f: *libc::FILE,
}

impl FILERes {
    pub fn new(f: *libc::FILE) -> FILERes {
        FILERes { f: f }
    }
}

impl Drop for FILERes {
    fn drop(&self) {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            libc::fclose(self.f);
        }
    }
}

pub fn FILE_reader(f: *libc::FILE, cleanup: bool) -> @Reader {
    if cleanup {
        @Wrapper { base: f, cleanup: FILERes::new(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)

/**
* Gives a `Reader` that allows you to read values from standard input.
*
* # Example
*
* ~~~ {.rust}
* let stdin = std::io::stdin();
* let line = stdin.read_line();
* std::io::print(line);
* ~~~
*/
pub fn stdin() -> @Reader {
    #[fixed_stack_segment]; #[inline(never)];

    unsafe {
        @rustrt::rust_get_stdin() as @Reader
    }
}

pub fn file_reader(path: &Path) -> Result<@Reader, ~str> {
    #[fixed_stack_segment]; #[inline(never)];

    let f = do path.with_c_str |pathbuf| {
        do "rb".with_c_str |modebuf| {
            unsafe { libc::fopen(pathbuf, modebuf as *libc::c_char) }
        }
    };

    if f as uint == 0u {
        Err(~"error opening " + path.to_str())
    } else {
        Ok(FILE_reader(f, true))
    }
}


// Byte readers
pub struct BytesReader {
    // FIXME(#5723) see other FIXME below
    // FIXME(#7268) this should also be parameterized over <'self>
    bytes: &'static [u8],
    pos: @mut uint
}

impl Reader for BytesReader {
    fn read(&self, bytes: &mut [u8], len: uint) -> uint {
        let count = num::min(len, self.bytes.len() - *self.pos);

        let view = self.bytes.slice(*self.pos, self.bytes.len());
        vec::bytes::copy_memory(bytes, view, count);

        *self.pos += count;

        count
    }

    fn read_byte(&self) -> int {
        if *self.pos == self.bytes.len() {
            return -1;
        }

        let b = self.bytes[*self.pos];
        *self.pos += 1u;
        b as int
    }

    fn eof(&self) -> bool {
        *self.pos == self.bytes.len()
    }

    fn seek(&self, offset: int, whence: SeekStyle) {
        let pos = *self.pos;
        *self.pos = seek_in_buf(offset, pos, self.bytes.len(), whence);
    }

    fn tell(&self) -> uint {
        *self.pos
    }
}

pub fn with_bytes_reader<T>(bytes: &[u8], f: &fn(@Reader) -> T) -> T {
    // XXX XXX XXX this is glaringly unsound
    // FIXME(#5723) Use a &Reader for the callback's argument. Should be:
    // fn with_bytes_reader<'r, T>(bytes: &'r [u8], f: &fn(&'r Reader) -> T) -> T
    let bytes: &'static [u8] = unsafe { cast::transmute(bytes) };
    f(@BytesReader {
        bytes: bytes,
        pos: @mut 0
    } as @Reader)
}

pub fn with_str_reader<T>(s: &str, f: &fn(@Reader) -> T) -> T {
    // FIXME(#5723): As above.
    with_bytes_reader(s.as_bytes(), f)
}

// Writing
pub enum FileFlag { Append, Create, Truncate, NoFlag, }

// What type of writer are we?
#[deriving(Eq)]
pub enum WriterType { Screen, File }

// FIXME (#2004): Seekable really should be orthogonal.
// FIXME (#2004): eventually u64
/// The raw underlying writer trait. All writers must implement this.
pub trait Writer {

    /// Write all of the given bytes.
    fn write(&self, v: &[u8]);

    /// Move the current position within the stream. The second parameter
    /// determines the position that the first parameter is relative to.
    fn seek(&self, int, SeekStyle);

    /// Return the current position within the stream.
    fn tell(&self) -> uint;

    /// Flush the output buffer for this stream (if there is one).
    fn flush(&self) -> int;

    /// Determine if this Writer is writing to a file or not.
    fn get_type(&self) -> WriterType;
}

impl Writer for @Writer {
    fn write(&self, v: &[u8]) { self.write(v) }
    fn seek(&self, a: int, b: SeekStyle) { self.seek(a, b) }
    fn tell(&self) -> uint { self.tell() }
    fn flush(&self) -> int { self.flush() }
    fn get_type(&self) -> WriterType { self.get_type() }
}

impl<W:Writer,C> Writer for Wrapper<W, C> {
    fn write(&self, bs: &[u8]) { self.base.write(bs); }
    fn seek(&self, off: int, style: SeekStyle) { self.base.seek(off, style); }
    fn tell(&self) -> uint { self.base.tell() }
    fn flush(&self) -> int { self.base.flush() }
    fn get_type(&self) -> WriterType { File }
}

impl Writer for *libc::FILE {
    fn write(&self, v: &[u8]) {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            do v.as_imm_buf |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");
                    error!("%s", os::last_os_error());
                    fail!();
                }
            }
        }
    }
    fn seek(&self, offset: int, whence: SeekStyle) {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            assert!(libc::fseek(*self,
                                     offset as c_long,
                                     convert_whence(whence)) == 0 as c_int);
        }
    }
    fn tell(&self) -> uint {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            libc::ftell(*self) as uint
        }
    }
    fn flush(&self) -> int {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            libc::fflush(*self) as int
        }
    }
    fn get_type(&self) -> WriterType {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            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 {
        @Wrapper { base: f, cleanup: FILERes::new(f) } as @Writer
    } else {
        @f as @Writer
    }
}

impl Writer for fd_t {
    fn write(&self, v: &[u8]) {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            let mut count = 0u;
            do v.as_imm_buf |vbuf, len| {
                while count < len {
                    let vb = ptr::offset(vbuf, count as int) as *c_void;
                    let nout = libc::write(*self, vb, len as size_t);
                    if nout < 0 as ssize_t {
                        error!("error writing buffer");
                        error!("%s", os::last_os_error());
                        fail!();
                    }
                    count += nout as uint;
                }
            }
        }
    }
    fn seek(&self, _offset: int, _whence: SeekStyle) {
        error!("need 64-bit foreign calls for seek, sorry");
        fail!();
    }
    fn tell(&self) -> uint {
        error!("need 64-bit foreign calls for tell, sorry");
        fail!();
    }
    fn flush(&self) -> int { 0 }
    fn get_type(&self) -> WriterType {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            if libc::isatty(*self) == 0 { File } else { Screen }
        }
    }
}

pub struct FdRes {
    fd: fd_t,
}

impl FdRes {
    pub fn new(fd: fd_t) -> FdRes {
        FdRes { fd: fd }
    }
}

impl Drop for FdRes {
    fn drop(&self) {
        #[fixed_stack_segment]; #[inline(never)];

        unsafe {
            libc::close(self.fd);
        }
    }
}

pub fn fd_writer(fd: fd_t, cleanup: bool) -> @Writer {
    if cleanup {
        @Wrapper { base: fd, cleanup: FdRes::new(fd) } as @Writer
    } else {
        @fd as @Writer
    }
}


pub fn mk_file_writer(path: &Path, flags: &[FileFlag])
                   -> Result<@Writer, ~str> {
    #[fixed_stack_segment]; #[inline(never)];

    #[cfg(windows)]
    fn wb() -> c_int {
      (O_WRONLY | libc::consts::os::extra::O_BINARY) as c_int
    }

    #[cfg(unix)]
    fn wb() -> c_int { O_WRONLY as c_int }

    let mut fflags: c_int = wb();
    for f in flags.iter() {
        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 = unsafe {
        do path.with_c_str |pathbuf| {
            libc::open(pathbuf, fflags, (S_IRUSR | S_IWUSR) as c_int)
        }
    };
    if fd < (0 as c_int) {
        Err(fmt!("error opening %s: %s", path.to_str(), os::last_os_error()))
    } else {
        Ok(fd_writer(fd, true))
    }
}

pub fn u64_to_le_bytes<T>(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] = ~[];
        let mut i = size;
        let mut 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<T>(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: &[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)
/// Generic utility functions defined on writers.
pub trait WriterUtil {

    /// Write a single utf-8 encoded char.
    fn write_char(&self, ch: char);

    /// Write every char in the given str, encoded as utf-8.
    fn write_str(&self, s: &str);

    /// Write the given str, as utf-8, followed by '\n'.
    fn write_line(&self, s: &str);

    /// Write the result of passing n through `int::to_str_bytes`.
    fn write_int(&self, n: int);

    /// Write the result of passing n through `uint::to_str_bytes`.
    fn write_uint(&self, n: uint);

    /// Write a little-endian uint (number of bytes depends on system).
    fn write_le_uint(&self, n: uint);

    /// Write a little-endian int (number of bytes depends on system).
    fn write_le_int(&self, n: int);

    /// Write a big-endian uint (number of bytes depends on system).
    fn write_be_uint(&self, n: uint);

    /// Write a big-endian int (number of bytes depends on system).
    fn write_be_int(&self, n: int);

    /// Write a big-endian u64 (8 bytes).
    fn write_be_u64(&self, n: u64);

    /// Write a big-endian u32 (4 bytes).
    fn write_be_u32(&self, n: u32);

    /// Write a big-endian u16 (2 bytes).
    fn write_be_u16(&self, n: u16);

    /// Write a big-endian i64 (8 bytes).
    fn write_be_i64(&self, n: i64);

    /// Write a big-endian i32 (4 bytes).
    fn write_be_i32(&self, n: i32);

    /// Write a big-endian i16 (2 bytes).
    fn write_be_i16(&self, n: i16);

    /// Write a big-endian IEEE754 double-precision floating-point (8 bytes).
    fn write_be_f64(&self, f: f64);

    /// Write a big-endian IEEE754 single-precision floating-point (4 bytes).
    fn write_be_f32(&self, f: f32);

    /// Write a little-endian u64 (8 bytes).
    fn write_le_u64(&self, n: u64);

    /// Write a little-endian u32 (4 bytes).
    fn write_le_u32(&self, n: u32);

    /// Write a little-endian u16 (2 bytes).
    fn write_le_u16(&self, n: u16);

    /// Write a little-endian i64 (8 bytes).
    fn write_le_i64(&self, n: i64);

    /// Write a little-endian i32 (4 bytes).
    fn write_le_i32(&self, n: i32);

    /// Write a little-endian i16 (2 bytes).
    fn write_le_i16(&self, n: i16);

    /// Write a little-endian IEEE754 double-precision floating-point
    /// (8 bytes).
    fn write_le_f64(&self, f: f64);

    /// Write a little-endian IEEE754 single-precision floating-point
    /// (4 bytes).
    fn write_le_f32(&self, f: f32);

    /// Write a u8 (1 byte).
    fn write_u8(&self, n: u8);

    /// Write a i8 (1 byte).
    fn write_i8(&self, n: i8);
}

impl<T:Writer> WriterUtil for T {
    fn write_char(&self, ch: char) {
        if (ch as uint) < 128u {
            self.write(&[ch as u8]);
        } else {
            self.write_str(str::from_char(ch));
        }
    }
    fn write_str(&self, s: &str) { self.write(s.as_bytes()) }
    fn write_line(&self, s: &str) {
        self.write_str(s);
        self.write_str(&"\n");
    }
    fn write_int(&self, n: int) {
        int::to_str_bytes(n, 10u, |bytes| self.write(bytes))
    }
    fn write_uint(&self, n: uint) {
        uint::to_str_bytes(n, 10u, |bytes| self.write(bytes))
    }
    fn write_le_uint(&self, n: uint) {
        u64_to_le_bytes(n as u64, uint::bytes, |v| self.write(v))
    }
    fn write_le_int(&self, n: int) {
        u64_to_le_bytes(n as u64, int::bytes, |v| self.write(v))
    }
    fn write_be_uint(&self, n: uint) {
        u64_to_be_bytes(n as u64, uint::bytes, |v| self.write(v))
    }
    fn write_be_int(&self, n: int) {
        u64_to_be_bytes(n as u64, int::bytes, |v| self.write(v))
    }
    fn write_be_u64(&self, n: u64) {
        u64_to_be_bytes(n, 8u, |v| self.write(v))
    }
    fn write_be_u32(&self, n: u32) {
        u64_to_be_bytes(n as u64, 4u, |v| self.write(v))
    }
    fn write_be_u16(&self, n: u16) {
        u64_to_be_bytes(n as u64, 2u, |v| self.write(v))
    }
    fn write_be_i64(&self, n: i64) {
        u64_to_be_bytes(n as u64, 8u, |v| self.write(v))
    }
    fn write_be_i32(&self, n: i32) {
        u64_to_be_bytes(n as u64, 4u, |v| self.write(v))
    }
    fn write_be_i16(&self, n: i16) {
        u64_to_be_bytes(n as u64, 2u, |v| self.write(v))
    }
    fn write_be_f64(&self, f:f64) {
        unsafe {
            self.write_be_u64(cast::transmute(f))
        }
    }
    fn write_be_f32(&self, f:f32) {
        unsafe {
            self.write_be_u32(cast::transmute(f))
        }
    }
    fn write_le_u64(&self, n: u64) {
        u64_to_le_bytes(n, 8u, |v| self.write(v))
    }
    fn write_le_u32(&self, n: u32) {
        u64_to_le_bytes(n as u64, 4u, |v| self.write(v))
    }
    fn write_le_u16(&self, n: u16) {
        u64_to_le_bytes(n as u64, 2u, |v| self.write(v))
    }
    fn write_le_i64(&self, n: i64) {
        u64_to_le_bytes(n as u64, 8u, |v| self.write(v))
    }
    fn write_le_i32(&self, n: i32) {
        u64_to_le_bytes(n as u64, 4u, |v| self.write(v))
    }
    fn write_le_i16(&self, n: i16) {
        u64_to_le_bytes(n as u64, 2u, |v| self.write(v))
    }
    fn write_le_f64(&self, f:f64) {
        unsafe {
            self.write_le_u64(cast::transmute(f))
        }
    }
    fn write_le_f32(&self, f:f32) {
        unsafe {
            self.write_le_u32(cast::transmute(f))
        }
    }

    fn write_u8(&self, n: u8) { self.write([n]) }
    fn write_i8(&self, n: i8) { self.write([n as u8]) }

}

pub fn file_writer(path: &Path, flags: &[FileFlag]) -> Result<@Writer, ~str> {
    mk_file_writer(path, flags).chain(|w| Ok(w))
}


// FIXME: fileflags // #2004
pub fn buffered_file_writer(path: &Path) -> Result<@Writer, ~str> {
    #[fixed_stack_segment]; #[inline(never)];

    unsafe {
        let f = do path.with_c_str |pathbuf| {
            do "w".with_c_str |modebuf| {
                libc::fopen(pathbuf, modebuf)
            }
        };
        return if f as uint == 0u {
            Err(~"error opening " + path.to_str())
        } else {
            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?


/**
* Gives a `Writer` which allows you to write to the standard output.
*
* # Example
*
* ~~~ {.rust}
* let stdout = std::io::stdout();
* stdout.write_str("hello\n");
* ~~~
*/
pub fn stdout() -> @Writer { fd_writer(libc::STDOUT_FILENO as c_int, false) }

/**
* Gives a `Writer` which allows you to write to standard error.
*
* # Example
*
* ~~~ {.rust}
* let stderr = std::io::stderr();
* stderr.write_str("hello\n");
* ~~~
*/
pub fn stderr() -> @Writer { fd_writer(libc::STDERR_FILENO as c_int, false) }

/**
* Prints a string to standard output.
*
* This string will not have an implicit newline at the end. If you want
* an implicit newline, please see `println`.
*
* # Example
*
* ~~~ {.rust}
* // print is imported into the prelude, and so is always available.
* print("hello");
* ~~~
*/
pub fn print(s: &str) {
    stdout().write_str(s);
}

/**
* Prints a string to standard output, followed by a newline.
*
* If you do not want an implicit newline, please see `print`.
*
* # Example
*
* ~~~ {.rust}
* // println is imported into the prelude, and so is always available.
* println("hello");
* ~~~
*/
pub fn println(s: &str) {
    stdout().write_line(s);
}

pub struct BytesWriter {
    bytes: @mut ~[u8],
    pos: @mut uint,
}

impl BytesWriter {
    pub fn new() -> BytesWriter {
        BytesWriter {
            bytes: @mut ~[],
            pos: @mut 0
        }
    }
}

impl Writer for BytesWriter {
    fn write(&self, v: &[u8]) {
        let v_len = v.len();

        let bytes = &mut *self.bytes;
        let count = num::max(bytes.len(), *self.pos + v_len);
        bytes.reserve(count);

        unsafe {
            vec::raw::set_len(bytes, count);

            let view = bytes.mut_slice(*self.pos, count);
            vec::bytes::copy_memory(view, v, v_len);
        }

        *self.pos += v_len;
    }

    fn seek(&self, offset: int, whence: SeekStyle) {
        let pos = *self.pos;
        let len = self.bytes.len();
        *self.pos = seek_in_buf(offset, pos, len, whence);
    }

    fn tell(&self) -> uint {
        *self.pos
    }

    fn flush(&self) -> int {
        0
    }

    fn get_type(&self) -> WriterType {
        File
    }
}

pub fn with_bytes_writer(f: &fn(@Writer)) -> ~[u8] {
    let wr = @BytesWriter::new();
    f(wr as @Writer);
    let @BytesWriter { bytes, _ } = wr;
    (*bytes).clone()
}

pub fn with_str_writer(f: &fn(@Writer)) -> ~str {
    str::from_bytes(with_bytes_writer(f))
}

// 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;
}

pub fn read_whole_file_str(file: &Path) -> Result<~str, ~str> {
    do read_whole_file(file).chain |bytes| {
        if str::is_utf8(bytes) {
            Ok(str::from_bytes(bytes))
        } else {
            Err(file.to_str() + " is not UTF-8")
        }
    }
}

// FIXME (#2004): implement this in a low-level way. Going through the
// abstractions is pointless.
pub fn read_whole_file(file: &Path) -> Result<~[u8], ~str> {
    do file_reader(file).chain |rdr| {
        Ok(rdr.read_whole_stream())
    }
}

// fsync related

pub mod fsync {
    use io::{FILERes, FdRes, fd_t};
    use libc;
    use ops::Drop;
    use option::{None, Option, Some};
    use os;

    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<t> {
        arg: Arg<t>,
    }

    impl <t> Res<t> {
        pub fn new(arg: Arg<t>) -> Res<t> {
            Res { arg: arg }
        }
    }

    #[unsafe_destructor]
    impl<T> Drop for Res<T> {
        fn drop(&self) {
            match self.arg.opt_level {
                None => (),
                Some(level) => {
                  // fail hard if not succesful
                  assert!(((self.arg.fsync_fn)(&self.arg.val, level) != -1));
                }
            }
        }
    }

    pub struct Arg<t> {
        val: t,
        opt_level: Option<Level>,
        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<Level>,
                         blk: &fn(v: Res<*libc::FILE>)) {
        blk(Res::new(Arg {
            val: file.f, opt_level: opt_level,
            fsync_fn: |file, l| fsync_fd(fileno(*file), l)
        }));

        fn fileno(stream: *libc::FILE) -> libc::c_int {
            #[fixed_stack_segment]; #[inline(never)];
            unsafe { libc::fileno(stream) }
        }
    }

    // fsync fd after executing blk
    pub fn fd_res_sync(fd: &FdRes, opt_level: Option<Level>,
                       blk: &fn(v: Res<fd_t>)) {
        blk(Res::new(Arg {
            val: fd.fd, opt_level: opt_level,
            fsync_fn: |fd, l| fsync_fd(*fd, l)
        }));
    }

    fn fsync_fd(fd: libc::c_int, level: Level) -> int {
        #[fixed_stack_segment]; #[inline(never)];

        os::fsync_fd(fd, level) as int
    }

    // Type of objects that may want to fsync
    pub trait FSyncable { fn fsync(&self, l: Level) -> int; }

    // Call o.fsync after executing blk
    pub fn obj_sync(o: @FSyncable, opt_level: Option<Level>,
                    blk: &fn(v: Res<@FSyncable>)) {
        blk(Res::new(Arg {
            val: o, opt_level: opt_level,
            fsync_fn: |o, l| (*o).fsync(l)
        }));
    }
}

#[cfg(test)]
mod tests {
    use prelude::*;
    use i32;
    use io::{BytesWriter, SeekCur, SeekEnd, SeekSet};
    use io;
    use path::Path;
    use result::{Ok, Err};
    use u64;
    use vec;

    #[test]
    fn test_simple() {
        let tmpfile = &Path("tmp/lib-io-test-simple.tmp");
        debug!(tmpfile);
        let frood: ~str =
            ~"A hoopy frood who really knows where his towel is.";
        debug!(frood.clone());
        {
            let out = io::file_writer(tmpfile, [io::Create, io::Truncate]).unwrap();
            out.write_str(frood);
        }
        let inp = io::file_reader(tmpfile).unwrap();
        let frood2: ~str = inp.read_c_str();
        debug!(frood2.clone());
        assert_eq!(frood, frood2);
    }

    #[test]
    fn test_each_byte_each_char_file() {
        // Issue #5056 -- shouldn't include trailing EOF.
        let path = Path("tmp/lib-io-test-each-byte-each-char-file.tmp");

        {
            // create empty, enough to reproduce a problem
            io::file_writer(&path, [io::Create]).unwrap();
        }

        {
            let file = io::file_reader(&path).unwrap();
            do file.each_byte() |_| {
                fail!("must be empty")
            };
        }

        {
            let file = io::file_reader(&path).unwrap();
            do file.each_char() |_| {
                fail!("must be empty")
            };
        }
    }

    #[test]
    fn test_readchars_empty() {
        do io::with_str_reader("") |inp| {
            let res : ~[char] = inp.read_chars(128);
            assert_eq!(res.len(), 0);
        }
    }

    #[test]
    fn test_read_line_utf8() {
        do io::with_str_reader("生锈的汤匙切肉汤hello生锈的汤匙切肉汤") |inp| {
            let line = inp.read_line();
            assert_eq!(line, ~"生锈的汤匙切肉汤hello生锈的汤匙切肉汤");
        }
    }

    #[test]
    fn test_read_lines() {
        do io::with_str_reader("a\nb\nc\n") |inp| {
            assert_eq!(inp.read_lines(), ~[~"a", ~"b", ~"c"]);
        }

        do io::with_str_reader("a\nb\nc") |inp| {
            assert_eq!(inp.read_lines(), ~[~"a", ~"b", ~"c"]);
        }

        do io::with_str_reader("") |inp| {
            assert!(inp.read_lines().is_empty());
        }
    }

    #[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 <= ivals.len() {
                    assert_eq!(res.len(), len);
                }
                for (iv, c) in ivals.iter().zip(res.iter()) {
                    assert!(*iv == *c 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_eq!(res as int, 29983);
        }
    }

    #[test]
    fn test_readchar_empty() {
        do io::with_str_reader("") |inp| {
            let res : char = inp.read_char();
            assert_eq!(res as int, -1);
        }
    }

    #[test]
    fn file_reader_not_exist() {
        match io::file_reader(&Path("not a file")) {
          Err(e) => {
            assert_eq!(e, ~"error opening not a file");
          }
          Ok(_) => fail!()
        }
    }

    #[test]
    #[should_fail]
    #[ignore(cfg(windows))]
    fn test_read_buffer_too_small() {
        let path = &Path("tmp/lib-io-test-read-buffer-too-small.tmp");
        // ensure the file exists
        io::file_writer(path, [io::Create]).unwrap();

        let file = io::file_reader(path).unwrap();
        let mut buf = vec::from_elem(5, 0u8);
        file.read(buf, 6); // this should fail because buf is too small
    }

    #[test]
    fn test_read_buffer_big_enough() {
        let path = &Path("tmp/lib-io-test-read-buffer-big-enough.tmp");
        // ensure the file exists
        io::file_writer(path, [io::Create]).unwrap();

        let file = io::file_reader(path).unwrap();
        let mut buf = vec::from_elem(5, 0u8);
        file.read(buf, 4); // this should succeed because buf is big enough
    }

    #[test]
    fn test_write_empty() {
        let file = io::file_writer(&Path("tmp/lib-io-test-write-empty.tmp"),
                                   [io::Create]).unwrap();
        file.write([]);
    }

    #[test]
    fn file_writer_bad_name() {
        match io::file_writer(&Path("?/?"), []) {
          Err(e) => {
            assert!(e.starts_with("error opening"));
          }
          Ok(_) => fail!()
        }
    }

    #[test]
    fn buffered_file_writer_bad_name() {
        match io::buffered_file_writer(&Path("?/?")) {
          Err(e) => {
            assert!(e.starts_with("error opening"));
          }
          Ok(_) => fail!()
        }
    }

    #[test]
    fn bytes_buffer_overwrite() {
        let wr = BytesWriter::new();
        wr.write([0u8, 1u8, 2u8, 3u8]);
        assert!(*wr.bytes == ~[0u8, 1u8, 2u8, 3u8]);
        wr.seek(-2, SeekCur);
        wr.write([4u8, 5u8, 6u8, 7u8]);
        assert!(*wr.bytes == ~[0u8, 1u8, 4u8, 5u8, 6u8, 7u8]);
        wr.seek(-2, SeekEnd);
        wr.write([8u8]);
        wr.seek(1, SeekSet);
        wr.write([9u8]);
        assert!(*wr.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]).unwrap();
            for i in uints.iter() {
                file.write_le_u64(*i);
            }
        }

        // then read them back and check that they are the same
        {
            let file = io::file_reader(&path).unwrap();
            for i in uints.iter() {
                assert_eq!(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]).unwrap();
            for i in uints.iter() {
                file.write_be_u64(*i);
            }
        }

        // then read them back and check that they are the same
        {
            let file = io::file_reader(&path).unwrap();
            for i in uints.iter() {
                assert_eq!(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]).unwrap();
            for i in ints.iter() {
                file.write_be_i32(*i);
            }
        }

        // then read them back and check that they are the same
        {
            let file = io::file_reader(&path).unwrap();
            for i in ints.iter() {
                // this tests that the sign extension is working
                // (comparing the values as i32 would not test this)
                assert_eq!(file.read_be_int_n(4), *i as i64);
            }
        }
    }

    #[test]
    fn test_read_f32() {
        let path = Path("tmp/lib-io-test-read-f32.tmp");
        //big-endian floating-point 8.1250
        let buf = ~[0x41, 0x02, 0x00, 0x00];

        {
            let file = io::file_writer(&path, [io::Create]).unwrap();
            file.write(buf);
        }

        {
            let file = io::file_reader(&path).unwrap();
            let f = file.read_be_f32();
            assert_eq!(f, 8.1250);
        }
    }

    #[test]
    fn test_read_write_f32() {
        let path = Path("tmp/lib-io-test-read-write-f32.tmp");
        let f:f32 = 8.1250;

        {
            let file = io::file_writer(&path, [io::Create]).unwrap();
            file.write_be_f32(f);
            file.write_le_f32(f);
        }

        {
            let file = io::file_reader(&path).unwrap();
            assert_eq!(file.read_be_f32(), 8.1250);
            assert_eq!(file.read_le_f32(), 8.1250);
        }
    }
}
