1
Fork 0

std: Implement stdio for std::io

This is an implementation of RFC 899 and adds stdio functionality to the new
`std::io` module. Details of the API can be found on the RFC, but from a high
level:

* `io::{stdin, stdout, stderr}` constructors are now available. There are also
  `*_raw` variants for unbuffered and unlocked access.
* All handles are globally shared (excluding raw variants).
* The stderr handle is no longer buffered.
* All handles can be explicitly locked (excluding the raw variants).

The `print!` and `println!` machinery has not yet been hooked up to these
streams just yet. The `std::fmt::output` module has also not yet been
implemented as part of this commit.
This commit is contained in:
Alex Crichton 2015-02-24 23:27:20 -08:00
parent 8a69110c3b
commit 94d71f8836
20 changed files with 732 additions and 108 deletions

View file

@ -201,10 +201,11 @@ impl<T> Arc<T> {
impl<T> Arc<T> { impl<T> Arc<T> {
#[inline] #[inline]
fn inner(&self) -> &ArcInner<T> { fn inner(&self) -> &ArcInner<T> {
// This unsafety is ok because while this arc is alive we're guaranteed that the inner // This unsafety is ok because while this arc is alive we're guaranteed
// pointer is valid. Furthermore, we know that the `ArcInner` structure itself is `Sync` // that the inner pointer is valid. Furthermore, we know that the
// because the inner data is `Sync` as well, so we're ok loaning out an immutable pointer // `ArcInner` structure itself is `Sync` because the inner data is
// to these contents. // `Sync` as well, so we're ok loaning out an immutable pointer to these
// contents.
unsafe { &**self._ptr } unsafe { &**self._ptr }
} }
} }
@ -236,13 +237,15 @@ impl<T> Clone for Arc<T> {
/// ``` /// ```
#[inline] #[inline]
fn clone(&self) -> Arc<T> { fn clone(&self) -> Arc<T> {
// Using a relaxed ordering is alright here, as knowledge of the original reference // Using a relaxed ordering is alright here, as knowledge of the
// prevents other threads from erroneously deleting the object. // original reference prevents other threads from erroneously deleting
// the object.
// //
// As explained in the [Boost documentation][1], Increasing the reference counter can // As explained in the [Boost documentation][1], Increasing the
// always be done with memory_order_relaxed: New references to an object can only be formed // reference counter can always be done with memory_order_relaxed: New
// from an existing reference, and passing an existing reference from one thread to another // references to an object can only be formed from an existing
// must already provide any required synchronization. // reference, and passing an existing reference from one thread to
// another must already provide any required synchronization.
// //
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
self.inner().strong.fetch_add(1, Relaxed); self.inner().strong.fetch_add(1, Relaxed);

59
src/libstd/io/lazy.rs Normal file
View file

@ -0,0 +1,59 @@
// Copyright 2015 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.
use prelude::v1::*;
use boxed;
use cell::UnsafeCell;
use rt;
use sync::{StaticMutex, Arc};
pub struct Lazy<T> {
pub lock: StaticMutex,
pub ptr: UnsafeCell<*mut Arc<T>>,
pub init: fn() -> Arc<T>,
}
unsafe impl<T> Sync for Lazy<T> {}
macro_rules! lazy_init {
($init:expr) => (::io::lazy::Lazy {
lock: ::sync::MUTEX_INIT,
ptr: ::cell::UnsafeCell { value: 0 as *mut _ },
init: $init,
})
}
impl<T: Send + Sync + 'static> Lazy<T> {
pub fn get(&'static self) -> Option<Arc<T>> {
let _g = self.lock.lock();
unsafe {
let mut ptr = *self.ptr.get();
if ptr.is_null() {
ptr = boxed::into_raw(self.init());
*self.ptr.get() = ptr;
} else if ptr as usize == 1 {
return None
}
Some((*ptr).clone())
}
}
fn init(&'static self) -> Box<Arc<T>> {
rt::at_exit(move || unsafe {
let g = self.lock.lock();
let ptr = *self.ptr.get();
*self.ptr.get() = 1 as *mut _;
drop(g);
drop(Box::from_raw(ptr))
});
Box::new((self.init)())
}
}

View file

@ -39,6 +39,10 @@ pub use self::buffered::IntoInnerError;
pub use self::cursor::Cursor; pub use self::cursor::Cursor;
pub use self::error::{Result, Error, ErrorKind}; pub use self::error::{Result, Error, ErrorKind};
pub use self::util::{copy, sink, Sink, empty, Empty, repeat, Repeat}; pub use self::util::{copy, sink, Sink, empty, Empty, repeat, Repeat};
pub use self::stdio::{stdin, stdout, stderr, Stdin, Stdout, Stderr};
pub use self::stdio::{StdoutLock, StderrLock, StdinLock};
#[macro_use] mod lazy;
pub mod prelude; pub mod prelude;
mod buffered; mod buffered;
@ -46,6 +50,7 @@ mod cursor;
mod error; mod error;
mod impls; mod impls;
mod util; mod util;
mod stdio;
const DEFAULT_BUF_SIZE: usize = 64 * 1024; const DEFAULT_BUF_SIZE: usize = 64 * 1024;

325
src/libstd/io/stdio.rs Normal file
View file

@ -0,0 +1,325 @@
// Copyright 2015 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.
use prelude::v1::*;
use io::prelude::*;
use cmp;
use fmt;
use io::lazy::Lazy;
use io::{self, BufReader, LineWriter};
use sync::{Arc, Mutex, MutexGuard};
use sys::stdio;
/// A handle to a raw instance of the standard input stream of this process.
///
/// This handle is not synchronized or buffered in any fashion. Constructed via
/// the `std::io::stdin_raw` function.
pub struct StdinRaw(stdio::Stdin);
/// A handle to a raw instance of the standard output stream of this process.
///
/// This handle is not synchronized or buffered in any fashion. Constructed via
/// the `std::io::stdout_raw` function.
pub struct StdoutRaw(stdio::Stdout);
/// A handle to a raw instance of the standard output stream of this process.
///
/// This handle is not synchronized or buffered in any fashion. Constructed via
/// the `std::io::stderr_raw` function.
pub struct StderrRaw(stdio::Stderr);
/// Construct a new raw handle to the standard input of this process.
///
/// The returned handle does not interact with any other handles created nor
/// handles returned by `std::io::stdin`. Data buffered by the `std::io::stdin`
/// handles is **not** available to raw handles returned from this function.
///
/// The returned handle has no external synchronization or buffering.
pub fn stdin_raw() -> StdinRaw { StdinRaw(stdio::Stdin::new()) }
/// Construct a new raw handle to the standard input stream of this process.
///
/// The returned handle does not interact with any other handles created nor
/// handles returned by `std::io::stdout`. Note that data is buffered by the
/// `std::io::stdin` handles so writes which happen via this raw handle may
/// appear before previous writes.
///
/// The returned handle has no external synchronization or buffering layered on
/// top.
pub fn stdout_raw() -> StdoutRaw { StdoutRaw(stdio::Stdout::new()) }
/// Construct a new raw handle to the standard input stream of this process.
///
/// The returned handle does not interact with any other handles created nor
/// handles returned by `std::io::stdout`.
///
/// The returned handle has no external synchronization or buffering layered on
/// top.
pub fn stderr_raw() -> StderrRaw { StderrRaw(stdio::Stderr::new()) }
impl Read for StdinRaw {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) }
}
impl Write for StdoutRaw {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
impl Write for StderrRaw {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) }
fn flush(&mut self) -> io::Result<()> { Ok(()) }
}
/// A handle to the standard input stream of a process.
///
/// Each handle is a shared reference to a global buffer of input data to this
/// process. A handle can be `lock`'d to gain full access to `BufRead` methods
/// (e.g. `.lines()`). Writes to this handle are otherwise locked with respect
/// to other writes.
///
/// This handle implements the `Read` trait, but beware that concurrent reads
/// of `Stdin` must be executed with care.
pub struct Stdin {
inner: Arc<Mutex<BufReader<StdinRaw>>>,
}
/// A locked reference to the a `Stdin` handle.
///
/// This handle implements both the `Read` and `BufRead` traits and is
/// constructed via the `lock` method on `Stdin`.
pub struct StdinLock<'a> {
inner: MutexGuard<'a, BufReader<StdinRaw>>,
}
/// Create a new handle to the global standard input stream of this process.
///
/// The handle returned refers to a globally shared buffer between all threads.
/// Access is synchronized and can be explicitly controlled with the `lock()`
/// method.
///
/// The `Read` trait is implemented for the returned value but the `BufRead`
/// trait is not due to the global nature of the standard input stream. The
/// locked version, `StdinLock`, implements both `Read` and `BufRead`, however.
///
/// To avoid locking and buffering altogether, it is recommended to use the
/// `stdin_raw` constructor.
pub fn stdin() -> Stdin {
static INSTANCE: Lazy<Mutex<BufReader<StdinRaw>>> = lazy_init!(stdin_init);
return Stdin {
inner: INSTANCE.get().expect("cannot access stdin during shutdown"),
};
fn stdin_init() -> Arc<Mutex<BufReader<StdinRaw>>> {
// The default buffer capacity is 64k, but apparently windows
// doesn't like 64k reads on stdin. See #13304 for details, but the
// idea is that on windows we use a slightly smaller buffer that's
// been seen to be acceptable.
Arc::new(Mutex::new(if cfg!(windows) {
BufReader::with_capacity(8 * 1024, stdin_raw())
} else {
BufReader::new(stdin_raw())
}))
}
}
impl Stdin {
/// Lock this handle to the standard input stream, returning a readable
/// guard.
///
/// The lock is released when the returned lock goes out of scope. The
/// returned guard also implements the `Read` and `BufRead` traits for
/// accessing the underlying data.
pub fn lock(&self) -> StdinLock {
StdinLock { inner: self.inner.lock().unwrap() }
}
}
impl Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.lock().read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<()> {
self.lock().read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<()> {
self.lock().read_to_string(buf)
}
}
impl<'a> Read for StdinLock<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// Flush stdout so that weird issues like a print!'d prompt not being
// shown until after the user hits enter.
drop(stdout().flush());
self.inner.read(buf)
}
}
impl<'a> BufRead for StdinLock<'a> {
fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() }
fn consume(&mut self, n: usize) { self.inner.consume(n) }
}
// As with stdin on windows, stdout often can't handle writes of large
// sizes. For an example, see #14940. For this reason, don't try to
// write the entire output buffer on windows. On unix we can just
// write the whole buffer all at once.
//
// For some other references, it appears that this problem has been
// encountered by others [1] [2]. We choose the number 8KB just because
// libuv does the same.
//
// [1]: https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232
// [2]: http://www.mail-archive.com/log4net-dev@logging.apache.org/msg00661.html
#[cfg(windows)]
const OUT_MAX: usize = 8192;
#[cfg(unix)]
const OUT_MAX: usize = ::usize::MAX;
/// A handle to the global standard output stream of the current process.
///
/// Each handle shares a global buffer of data to be written to the standard
/// output stream. Access is also synchronized via a lock and explicit control
/// over locking is available via the `lock` method.
pub struct Stdout {
// FIXME: this should be LineWriter or BufWriter depending on the state of
// stdout (tty or not). Note that if this is not line buffered it
// should also flush-on-panic or some form of flush-on-abort.
inner: Arc<Mutex<LineWriter<StdoutRaw>>>,
}
/// A locked reference to the a `Stdout` handle.
///
/// This handle implements the `Write` trait and is constructed via the `lock`
/// method on `Stdout`.
pub struct StdoutLock<'a> {
inner: MutexGuard<'a, LineWriter<StdoutRaw>>,
}
/// Constructs a new reference to the standard output of the current process.
///
/// Each handle returned is a reference to a shared global buffer whose access
/// is synchronized via a mutex. Explicit control over synchronization is
/// provided via the `lock` method.
///
/// The returned handle implements the `Write` trait.
///
/// To avoid locking and buffering altogether, it is recommended to use the
/// `stdout_raw` constructor.
pub fn stdout() -> Stdout {
static INSTANCE: Lazy<Mutex<LineWriter<StdoutRaw>>> = lazy_init!(stdout_init);
return Stdout {
inner: INSTANCE.get().expect("cannot access stdout during shutdown"),
};
fn stdout_init() -> Arc<Mutex<LineWriter<StdoutRaw>>> {
Arc::new(Mutex::new(LineWriter::new(stdout_raw())))
}
}
impl Stdout {
/// Lock this handle to the standard output stream, returning a writable
/// guard.
///
/// The lock is released when the returned lock goes out of scope. The
/// returned guard also implements the `Write` trait for writing data.
pub fn lock(&self) -> StdoutLock {
StdoutLock { inner: self.inner.lock().unwrap() }
}
}
impl Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.lock().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.lock().flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.lock().write_all(buf)
}
fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
self.lock().write_fmt(fmt)
}
}
impl<'a> Write for StdoutLock<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(&buf[..cmp::min(buf.len(), OUT_MAX)])
}
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
}
/// A handle to the standard error stream of a process.
///
/// For more information, see `stderr`
pub struct Stderr {
inner: Arc<Mutex<StderrRaw>>,
}
/// A locked reference to the a `Stderr` handle.
///
/// This handle implements the `Write` trait and is constructed via the `lock`
/// method on `Stderr`.
pub struct StderrLock<'a> {
inner: MutexGuard<'a, StderrRaw>,
}
/// Constructs a new reference to the standard error stream of a process.
///
/// Each returned handle is synchronized amongst all other handles created from
/// this function. No handles are buffered, however.
///
/// The returned handle implements the `Write` trait.
///
/// To avoid locking altogether, it is recommended to use the `stderr_raw`
/// constructor.
pub fn stderr() -> Stderr {
static INSTANCE: Lazy<Mutex<StderrRaw>> = lazy_init!(stderr_init);
return Stderr {
inner: INSTANCE.get().expect("cannot access stderr during shutdown"),
};
fn stderr_init() -> Arc<Mutex<StderrRaw>> {
Arc::new(Mutex::new(stderr_raw()))
}
}
impl Stderr {
/// Lock this handle to the standard error stream, returning a writable
/// guard.
///
/// The lock is released when the returned lock goes out of scope. The
/// returned guard also implements the `Write` trait for writing data.
pub fn lock(&self) -> StderrLock {
StderrLock { inner: self.inner.lock().unwrap() }
}
}
impl Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.lock().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.lock().flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.lock().write_all(buf)
}
fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
self.lock().write_fmt(fmt)
}
}
impl<'a> Write for StderrLock<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(&buf[..cmp::min(buf.len(), OUT_MAX)])
}
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
}

View file

@ -224,10 +224,10 @@ pub fn stdin() -> StdinReader {
unsafe { unsafe {
ONCE.call_once(|| { ONCE.call_once(|| {
// The default buffer capacity is 64k, but apparently windows doesn't like // The default buffer capacity is 64k, but apparently windows
// 64k reads on stdin. See #13304 for details, but the idea is that on // doesn't like 64k reads on stdin. See #13304 for details, but the
// windows we use a slightly smaller buffer that's been seen to be // idea is that on windows we use a slightly smaller buffer that's
// acceptable. // been seen to be acceptable.
let stdin = if cfg!(windows) { let stdin = if cfg!(windows) {
BufferedReader::with_capacity(8 * 1024, stdin_raw()) BufferedReader::with_capacity(8 * 1024, stdin_raw())
} else { } else {

View file

@ -63,6 +63,7 @@ pub mod time;
pub mod timer; pub mod timer;
pub mod tty; pub mod tty;
pub mod udp; pub mod udp;
pub mod stdio;
pub mod addrinfo { pub mod addrinfo {
pub use sys_common::net::get_host_addresses; pub use sys_common::net::get_host_addresses;

View file

@ -0,0 +1,52 @@
// Copyright 2015 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.
use prelude::v1::*;
use io;
use libc;
use sys::fd::FileDesc;
pub struct Stdin(());
pub struct Stdout(());
pub struct Stderr(());
impl Stdin {
pub fn new() -> Stdin { Stdin(()) }
pub fn read(&self, data: &mut [u8]) -> io::Result<usize> {
let fd = FileDesc::new(libc::STDIN_FILENO);
let ret = fd.read(data);
fd.into_raw();
return ret;
}
}
impl Stdout {
pub fn new() -> Stdout { Stdout(()) }
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
let fd = FileDesc::new(libc::STDOUT_FILENO);
let ret = fd.write(data);
fd.into_raw();
return ret;
}
}
impl Stderr {
pub fn new() -> Stderr { Stderr(()) }
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
let fd = FileDesc::new(libc::STDERR_FILENO);
let ret = fd.write(data);
fd.into_raw();
return ret;
}
}

View file

@ -48,6 +48,11 @@ pub const WSAESHUTDOWN: libc::c_int = 10058;
pub const ERROR_NO_MORE_FILES: libc::DWORD = 18; pub const ERROR_NO_MORE_FILES: libc::DWORD = 18;
pub const TOKEN_READ: libc::DWORD = 0x20008; pub const TOKEN_READ: libc::DWORD = 0x20008;
// Note that these are not actually HANDLEs, just values to pass to GetStdHandle
pub const STD_INPUT_HANDLE: libc::DWORD = -10;
pub const STD_OUTPUT_HANDLE: libc::DWORD = -11;
pub const STD_ERROR_HANDLE: libc::DWORD = -12;
#[repr(C)] #[repr(C)]
#[cfg(target_arch = "x86")] #[cfg(target_arch = "x86")]
pub struct WSADATA { pub struct WSADATA {
@ -427,6 +432,7 @@ extern "system" {
DesiredAccess: libc::DWORD, DesiredAccess: libc::DWORD,
TokenHandle: *mut libc::HANDLE) -> libc::BOOL; TokenHandle: *mut libc::HANDLE) -> libc::BOOL;
pub fn GetCurrentProcess() -> libc::HANDLE; pub fn GetCurrentProcess() -> libc::HANDLE;
pub fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
} }
#[link(name = "userenv")] #[link(name = "userenv")]

View file

@ -10,9 +10,10 @@
use prelude::v1::*; use prelude::v1::*;
use libc::{self, HANDLE};
use io;
use io::ErrorKind; use io::ErrorKind;
use io;
use libc::{self, HANDLE};
use mem;
use ptr; use ptr;
use sys::cvt; use sys::cvt;
@ -28,6 +29,12 @@ impl Handle {
pub fn raw(&self) -> HANDLE { self.0 } pub fn raw(&self) -> HANDLE { self.0 }
pub fn into_raw(self) -> HANDLE {
let ret = self.0;
unsafe { mem::forget(self) }
return ret;
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
read(self.0, buf) read(self.0, buf)
} }

View file

@ -61,6 +61,7 @@ pub mod time;
pub mod timer; pub mod timer;
pub mod tty; pub mod tty;
pub mod udp; pub mod udp;
pub mod stdio;
pub mod addrinfo { pub mod addrinfo {
pub use sys_common::net::get_host_addresses; pub use sys_common::net::get_host_addresses;

View file

@ -0,0 +1,155 @@
// Copyright 2015 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.
use prelude::v1::*;
use io::prelude::*;
use io::{self, Cursor};
use iter::repeat;
use libc;
use ptr;
use str;
use sync::Mutex;
use sys::c;
use sys::cvt;
use sys::handle::Handle;
struct NoClose(Option<Handle>);
enum Output {
Console(NoClose),
Pipe(NoClose),
}
pub struct Stdin {
handle: Output,
utf8: Mutex<io::Cursor<Vec<u8>>>,
}
pub struct Stdout(Output);
pub struct Stderr(Output);
fn get(handle: libc::DWORD) -> io::Result<Output> {
let handle = unsafe { c::GetStdHandle(handle) };
if handle == libc::INVALID_HANDLE_VALUE {
Err(io::Error::last_os_error())
} else if handle.is_null() {
Err(io::Error::new(io::ErrorKind::Other,
"no stdio handle available for this process", None))
} else {
let ret = NoClose::new(handle);
let mut out = 0;
match unsafe { c::GetConsoleMode(handle, &mut out) } {
0 => Ok(Output::Pipe(ret)),
_ => Ok(Output::Console(ret)),
}
}
}
fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
let handle = match *out {
Output::Console(ref c) => c.get().raw(),
Output::Pipe(ref p) => return p.get().write(data),
};
let utf16 = match str::from_utf8(data).ok() {
Some(utf8) => utf8.utf16_units().collect::<Vec<u16>>(),
None => return Err(invalid_encoding()),
};
let mut written = 0;
try!(cvt(unsafe {
c::WriteConsoleW(handle,
utf16.as_ptr() as libc::LPCVOID,
utf16.len() as u32,
&mut written,
ptr::null_mut())
}));
// FIXME if this only partially writes the utf16 buffer then we need to
// figure out how many bytes of `data` were actually written
assert_eq!(written as usize, utf16.len());
Ok(data.len())
}
impl Stdin {
pub fn new() -> Stdin {
Stdin {
handle: get(c::STD_INPUT_HANDLE).unwrap(),
utf8: Mutex::new(Cursor::new(Vec::new())),
}
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let handle = match self.handle {
Output::Console(ref c) => c.get().raw(),
Output::Pipe(ref p) => return p.get().read(buf),
};
let mut utf8 = self.utf8.lock().unwrap();
// Read more if the buffer is empty
if utf8.position() as usize == utf8.get_ref().len() {
let mut utf16: Vec<u16> = repeat(0u16).take(0x1000).collect();
let mut num = 0;
try!(cvt(unsafe {
c::ReadConsoleW(handle,
utf16.as_mut_ptr() as libc::LPVOID,
utf16.len() as u32,
&mut num,
ptr::null_mut())
}));
utf16.truncate(num as usize);
// FIXME: what to do about this data that has already been read?
let data = match String::from_utf16(&utf16) {
Ok(utf8) => utf8.into_bytes(),
Err(..) => return Err(invalid_encoding()),
};
*utf8 = Cursor::new(data);
}
// MemReader shouldn't error here since we just filled it
utf8.read(buf)
}
}
impl Stdout {
pub fn new() -> Stdout {
Stdout(get(c::STD_OUTPUT_HANDLE).unwrap())
}
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
write(&self.0, data)
}
}
impl Stderr {
pub fn new() -> Stderr {
Stderr(get(c::STD_ERROR_HANDLE).unwrap())
}
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
write(&self.0, data)
}
}
impl NoClose {
fn new(handle: libc::HANDLE) -> NoClose {
NoClose(Some(Handle::new(handle)))
}
fn get(&self) -> &Handle { self.0.as_ref().unwrap() }
}
impl Drop for NoClose {
fn drop(&mut self) {
self.0.take().unwrap().into_raw();
}
}
fn invalid_encoding() -> io::Error {
io::Error::new(io::ErrorKind::InvalidInput, "text was not valid unicode",
None)
}

View file

@ -43,12 +43,16 @@
#![feature(box_syntax)] #![feature(box_syntax)]
use std::ascii::OwnedAsciiExt; use std::ascii::OwnedAsciiExt;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io;
use std::slice; use std::slice;
use std::sync::Arc; use std::sync::Arc;
use std::thread; use std::thread;
static TABLE: [u8;4] = [ 'A' as u8, 'C' as u8, 'G' as u8, 'T' as u8 ]; static TABLE: [u8;4] = [ 'A' as u8, 'C' as u8, 'G' as u8, 'T' as u8 ];
static TABLE_SIZE: uint = 2 << 16; static TABLE_SIZE: usize = 2 << 16;
static OCCURRENCES: [&'static str;5] = [ static OCCURRENCES: [&'static str;5] = [
"GGT", "GGT",
@ -73,7 +77,7 @@ impl Code {
Code((self.hash() << 2) + (pack_symbol(c) as u64)) Code((self.hash() << 2) + (pack_symbol(c) as u64))
} }
fn rotate(&self, c: u8, frame: uint) -> Code { fn rotate(&self, c: u8, frame: usize) -> Code {
Code(self.push_char(c).hash() & ((1u64 << (2 * frame)) - 1)) Code(self.push_char(c).hash() & ((1u64 << (2 * frame)) - 1))
} }
@ -81,7 +85,7 @@ impl Code {
string.bytes().fold(Code(0u64), |a, b| a.push_char(b)) string.bytes().fold(Code(0u64), |a, b| a.push_char(b))
} }
fn unpack(&self, frame: uint) -> String { fn unpack(&self, frame: usize) -> String {
let mut key = self.hash(); let mut key = self.hash();
let mut result = Vec::new(); let mut result = Vec::new();
for _ in 0..frame { for _ in 0..frame {
@ -113,13 +117,13 @@ struct PrintCallback(&'static str);
impl TableCallback for PrintCallback { impl TableCallback for PrintCallback {
fn f(&self, entry: &mut Entry) { fn f(&self, entry: &mut Entry) {
let PrintCallback(s) = *self; let PrintCallback(s) = *self;
println!("{}\t{}", entry.count as int, s); println!("{}\t{}", entry.count, s);
} }
} }
struct Entry { struct Entry {
code: Code, code: Code,
count: uint, count: usize,
next: Option<Box<Entry>>, next: Option<Box<Entry>>,
} }
@ -165,20 +169,20 @@ impl Table {
let index = key.hash() % (TABLE_SIZE as u64); let index = key.hash() % (TABLE_SIZE as u64);
{ {
if self.items[index as uint].is_none() { if self.items[index as usize].is_none() {
let mut entry = box Entry { let mut entry = box Entry {
code: key, code: key,
count: 0, count: 0,
next: None, next: None,
}; };
c.f(&mut *entry); c.f(&mut *entry);
self.items[index as uint] = Some(entry); self.items[index as usize] = Some(entry);
return; return;
} }
} }
{ {
let entry = self.items[index as uint].as_mut().unwrap(); let entry = self.items[index as usize].as_mut().unwrap();
if entry.code == key { if entry.code == key {
c.f(&mut **entry); c.f(&mut **entry);
return; return;
@ -233,10 +237,10 @@ fn pack_symbol(c: u8) -> u8 {
} }
fn unpack_symbol(c: u8) -> u8 { fn unpack_symbol(c: u8) -> u8 {
TABLE[c as uint] TABLE[c as usize]
} }
fn generate_frequencies(mut input: &[u8], frame: uint) -> Table { fn generate_frequencies(mut input: &[u8], frame: usize) -> Table {
let mut frequencies = Table::new(); let mut frequencies = Table::new();
if input.len() < frame { return frequencies; } if input.len() < frame { return frequencies; }
let mut code = Code(0); let mut code = Code(0);
@ -256,7 +260,7 @@ fn generate_frequencies(mut input: &[u8], frame: uint) -> Table {
frequencies frequencies
} }
fn print_frequencies(frequencies: &Table, frame: uint) { fn print_frequencies(frequencies: &Table, frame: usize) {
let mut vector = Vec::new(); let mut vector = Vec::new();
for entry in frequencies.iter() { for entry in frequencies.iter() {
vector.push((entry.count, entry.code)); vector.push((entry.count, entry.code));
@ -280,9 +284,9 @@ fn print_occurrences(frequencies: &mut Table, occurrence: &'static str) {
frequencies.lookup(Code::pack(occurrence), PrintCallback(occurrence)) frequencies.lookup(Code::pack(occurrence), PrintCallback(occurrence))
} }
fn get_sequence<R: Buffer>(r: &mut R, key: &str) -> Vec<u8> { fn get_sequence<R: BufRead>(r: &mut R, key: &str) -> Vec<u8> {
let mut res = Vec::new(); let mut res = Vec::new();
for l in r.lines().map(|l| l.ok().unwrap()) for l in r.lines().map(|l| l.unwrap())
.skip_while(|l| key != &l[..key.len()]).skip(1) .skip_while(|l| key != &l[..key.len()]).skip(1)
{ {
res.push_all(l.trim().as_bytes()); res.push_all(l.trim().as_bytes());
@ -291,13 +295,13 @@ fn get_sequence<R: Buffer>(r: &mut R, key: &str) -> Vec<u8> {
} }
fn main() { fn main() {
let input = if std::env::var_os("RUST_BENCH").is_some() { let input = if env::var_os("RUST_BENCH").is_some() {
let fd = std::old_io::File::open(&Path::new("shootout-k-nucleotide.data")); let f = File::open("shootout-k-nucleotide.data").unwrap();
get_sequence(&mut std::old_io::BufferedReader::new(fd), ">THREE") get_sequence(&mut io::BufReader::new(f), ">THREE")
} else { } else {
let mut stdin = std::old_io::stdin(); let stdin = io::stdin();
let mut stdin = stdin.lock(); let mut stdin = stdin.lock();
get_sequence(&mut *stdin, ">THREE") get_sequence(&mut stdin, ">THREE")
}; };
let input = Arc::new(input); let input = Arc::new(input);

View file

@ -13,9 +13,8 @@
#![feature(box_syntax)] #![feature(box_syntax)]
#![allow(non_snake_case)] #![allow(non_snake_case)]
use std::old_io::BufferedReader; use std::io::prelude::*;
use std::old_io::stdio::StdReader; use std::io;
use std::old_io;
use std::iter::repeat; use std::iter::repeat;
use std::num::Int; use std::num::Int;
use std::env; use std::env;
@ -37,7 +36,7 @@ use std::env;
// //
// internal type of sudoku grids // internal type of sudoku grids
type grid = Vec<Vec<u8> > ; type grid = Vec<Vec<u8>>;
struct Sudoku { struct Sudoku {
grid: grid grid: grid
@ -55,9 +54,11 @@ impl Sudoku {
return Sudoku::new(g) return Sudoku::new(g)
} }
pub fn read(mut reader: &mut BufferedReader<StdReader>) -> Sudoku { pub fn read(reader: &mut BufRead) -> Sudoku {
/* assert first line is exactly "9,9" */ /* assert first line is exactly "9,9" */
assert!(reader.read_line().unwrap() == "9,9".to_string()); let mut s = String::new();
reader.read_line(&mut s).unwrap();
assert_eq!(s, "9,9\n");
let mut g = repeat(vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]) let mut g = repeat(vec![0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8])
.take(10).collect::<Vec<_>>(); .take(10).collect::<Vec<_>>();
@ -71,7 +72,7 @@ impl Sudoku {
if comps.len() == 3 { if comps.len() == 3 {
let row = comps[0].parse::<u8>().unwrap(); let row = comps[0].parse::<u8>().unwrap();
let col = comps[1].parse::<u8>().unwrap(); let col = comps[1].parse::<u8>().unwrap();
g[row as uint][col as uint] = comps[2].parse().unwrap(); g[row as usize][col as usize] = comps[2].parse().unwrap();
} }
else { else {
panic!("Invalid sudoku file"); panic!("Invalid sudoku file");
@ -80,11 +81,11 @@ impl Sudoku {
return Sudoku::new(g) return Sudoku::new(g)
} }
pub fn write(&self, writer: &mut old_io::Writer) { pub fn write(&self, writer: &mut Write) {
for row in 0u8..9u8 { for row in 0u8..9u8 {
write!(writer, "{}", self.grid[row as uint][0]); write!(writer, "{}", self.grid[row as usize][0]);
for col in 1u8..9u8 { for col in 1u8..9u8 {
write!(writer, " {}", self.grid[row as uint][col as uint]); write!(writer, " {}", self.grid[row as usize][col as usize]);
} }
write!(writer, "\n"); write!(writer, "\n");
} }
@ -95,7 +96,7 @@ impl Sudoku {
let mut work: Vec<(u8, u8)> = Vec::new(); /* queue of uncolored fields */ let mut work: Vec<(u8, u8)> = Vec::new(); /* queue of uncolored fields */
for row in 0u8..9u8 { for row in 0u8..9u8 {
for col in 0u8..9u8 { for col in 0u8..9u8 {
let color = self.grid[row as uint][col as uint]; let color = self.grid[row as usize][col as usize];
if color == 0u8 { if color == 0u8 {
work.push((row, col)); work.push((row, col));
} }
@ -107,7 +108,7 @@ impl Sudoku {
while ptr < end { while ptr < end {
let (row, col) = work[ptr]; let (row, col) = work[ptr];
// is there another color to try? // is there another color to try?
let the_color = self.grid[row as uint][col as uint] + let the_color = self.grid[row as usize][col as usize] +
(1 as u8); (1 as u8);
if self.next_color(row, col, the_color) { if self.next_color(row, col, the_color) {
// yes: advance work list // yes: advance work list
@ -130,10 +131,10 @@ impl Sudoku {
// find first remaining color that is available // find first remaining color that is available
let next = avail.next(); let next = avail.next();
self.grid[row as uint][col as uint] = next; self.grid[row as usize][col as usize] = next;
return 0u8 != next; return 0u8 != next;
} }
self.grid[row as uint][col as uint] = 0u8; self.grid[row as usize][col as usize] = 0u8;
return false; return false;
} }
@ -141,9 +142,9 @@ impl Sudoku {
fn drop_colors(&mut self, avail: &mut Colors, row: u8, col: u8) { fn drop_colors(&mut self, avail: &mut Colors, row: u8, col: u8) {
for idx in 0u8..9u8 { for idx in 0u8..9u8 {
/* check same column fields */ /* check same column fields */
avail.remove(self.grid[idx as uint][col as uint]); avail.remove(self.grid[idx as usize][col as usize]);
/* check same row fields */ /* check same row fields */
avail.remove(self.grid[row as uint][idx as uint]); avail.remove(self.grid[row as usize][idx as usize]);
} }
// check same block fields // check same block fields
@ -151,7 +152,7 @@ impl Sudoku {
let col0 = (col / 3u8) * 3u8; let col0 = (col / 3u8) * 3u8;
for alt_row in row0..row0 + 3u8 { for alt_row in row0..row0 + 3u8 {
for alt_col in col0..col0 + 3u8 { for alt_col in col0..col0 + 3u8 {
avail.remove(self.grid[alt_row as uint][alt_col as uint]); avail.remove(self.grid[alt_row as usize][alt_col as usize]);
} }
} }
} }
@ -165,7 +166,7 @@ static HEADS: u16 = (1u16 << 10) - 1; /* bits 9..0 */
impl Colors { impl Colors {
fn new(start_color: u8) -> Colors { fn new(start_color: u8) -> Colors {
// Sets bits 9..start_color // Sets bits 9..start_color
let tails = !0u16 << start_color as uint; let tails = !0u16 << start_color as usize;
return Colors(HEADS & tails); return Colors(HEADS & tails);
} }
@ -182,7 +183,7 @@ impl Colors {
fn remove(&mut self, color: u8) { fn remove(&mut self, color: u8) {
if color != 0u8 { if color != 0u8 {
let Colors(val) = *self; let Colors(val) = *self;
let mask = !(1u16 << color as uint); let mask = !(1u16 << color as usize);
*self = Colors(val & mask); *self = Colors(val & mask);
} }
} }
@ -269,15 +270,16 @@ fn check_DEFAULT_SUDOKU_solution() {
} }
fn main() { fn main() {
let args = env::args(); let args = env::args();
let use_default = args.len() == 1; let use_default = args.len() == 1;
let mut sudoku = if use_default { let mut sudoku = if use_default {
Sudoku::from_vec(&DEFAULT_SUDOKU) Sudoku::from_vec(&DEFAULT_SUDOKU)
} else { } else {
let mut stdin = old_io::stdin(); let stdin = io::stdin();
let mut stdin = stdin.lock(); let mut locked = stdin.lock();
Sudoku::read(&mut *stdin) Sudoku::read(&mut locked)
}; };
sudoku.solve(); sudoku.solve();
sudoku.write(&mut old_io::stdout()); let out = io::stdout();
sudoku.write(&mut out.lock());
} }

View file

@ -10,4 +10,5 @@
fn main() { fn main() {
let _ = std::old_io::stdin(); let _ = std::old_io::stdin();
let _ = std::io::stdin();
} }

View file

@ -8,10 +8,10 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// ignore-fast
use std::env; use std::env;
use std::old_io; use std::io::prelude::*;
use std::io;
use std::process::{Command, Stdio};
use std::str; use std::str;
fn main() { fn main() {
@ -25,17 +25,19 @@ fn main() {
fn parent() { fn parent() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let mut p = old_io::process::Command::new(&args[0]) let mut p = Command::new(&args[0]).arg("child")
.arg("child").spawn().unwrap(); .stdout(Stdio::capture())
p.stdin.as_mut().unwrap().write_str("test1\ntest2\ntest3").unwrap(); .stdin(Stdio::capture())
.spawn().unwrap();
p.stdin.as_mut().unwrap().write_all(b"test1\ntest2\ntest3").unwrap();
let out = p.wait_with_output().unwrap(); let out = p.wait_with_output().unwrap();
assert!(out.status.success()); assert!(out.status.success());
let s = str::from_utf8(&out.output).unwrap(); let s = str::from_utf8(&out.stdout).unwrap();
assert_eq!(s, "test1\n\ntest2\n\ntest3\n"); assert_eq!(s, "test1\ntest2\ntest3\n");
} }
fn child() { fn child() {
let mut stdin = old_io::stdin(); let mut stdin = io::stdin();
for line in stdin.lock().lines() { for line in stdin.lock().lines() {
println!("{}", line.unwrap()); println!("{}", line.unwrap());
} }

View file

@ -8,11 +8,10 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::old_io::process;
use std::old_io::Command;
use std::old_io;
use std::env; use std::env;
use std::io::prelude::*;
use std::io;
use std::process::{Command, Stdio};
fn main() { fn main() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
@ -21,22 +20,23 @@ fn main() {
} }
test(); test();
} }
fn child() { fn child() {
old_io::stdout().write_line("foo").unwrap(); writeln!(&mut io::stdout(), "foo").unwrap();
old_io::stderr().write_line("bar").unwrap(); writeln!(&mut io::stderr(), "bar").unwrap();
let mut stdin = old_io::stdin(); let mut stdin = io::stdin();
assert_eq!(stdin.lock().read_line().err().unwrap().kind, old_io::EndOfFile); let mut s = String::new();
stdin.lock().read_line(&mut s).unwrap();
assert_eq!(s.len(), 0);
} }
fn test() { fn test() {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let mut p = Command::new(&args[0]).arg("child") let mut p = Command::new(&args[0]).arg("child")
.stdin(process::Ignored) .stdin(Stdio::capture())
.stdout(process::Ignored) .stdout(Stdio::capture())
.stderr(process::Ignored) .stderr(Stdio::capture())
.spawn().unwrap(); .spawn().unwrap();
assert!(p.wait().unwrap().success()); assert!(p.wait().unwrap().success());
} }

View file

@ -17,11 +17,13 @@
// A var moved into a proc, that has a mutable loan path should // A var moved into a proc, that has a mutable loan path should
// not trigger a misleading unused_mut warning. // not trigger a misleading unused_mut warning.
use std::io::prelude::*;
use std::thread; use std::thread;
pub fn main() { pub fn main() {
let mut stdin = std::old_io::stdin(); let mut stdin = std::io::stdin();
thread::spawn(move|| { thread::spawn(move|| {
let _ = stdin.read_to_end(); let mut v = Vec::new();
}); let _ = stdin.read_to_end(&mut v);
}).join().ok().unwrap();
} }

View file

@ -11,13 +11,13 @@
#![allow(unknown_features)] #![allow(unknown_features)]
#![feature(box_syntax)] #![feature(box_syntax)]
use std::old_io; use std::io::{self, Write};
fn f(wr: &mut Writer) { fn f(wr: &mut Write) {
wr.write_str("hello").ok().expect("failed"); wr.write_all(b"hello").ok().expect("failed");
} }
fn main() { fn main() {
let mut wr = box old_io::stdout() as Box<Writer + 'static>; let mut wr = box io::stdout() as Box<Write>;
f(&mut wr); f(&mut wr);
} }

View file

@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::old_io; use std::io;
pub fn main() { pub fn main() {
let stdout = &mut old_io::stdout() as &mut old_io::Writer; let stdout = &mut io::stdout() as &mut io::Write;
stdout.write(b"Hello!"); stdout.write(b"Hello!");
} }

View file

@ -16,30 +16,31 @@
// non-ASCII characters. The child process ensures all the strings are // non-ASCII characters. The child process ensures all the strings are
// intact. // intact.
use std::old_io; use std::io::prelude::*;
use std::old_io::fs; use std::io;
use std::old_io::Command; use std::fs;
use std::process::Command;
use std::os; use std::os;
use std::env; use std::env;
use std::old_path::Path; use std::path::{Path, PathBuf};
fn main() { fn main() {
let my_args = env::args().collect::<Vec<_>>(); let my_args = env::args().collect::<Vec<_>>();
let my_cwd = os::getcwd().unwrap(); let my_cwd = PathBuf::new(os::getcwd().unwrap().as_str().unwrap());
let my_env = env::vars().collect::<Vec<_>>(); let my_env = env::vars().collect::<Vec<_>>();
let my_path = Path::new(os::self_exe_name().unwrap()); let my_path = PathBuf::new(os::self_exe_name().unwrap().as_str().unwrap());
let my_dir = my_path.dir_path(); let my_dir = my_path.parent().unwrap();
let my_ext = my_path.extension_str().unwrap_or(""); let my_ext = my_path.extension().and_then(|s| s.to_str()).unwrap_or("");
// some non-ASCII characters // some non-ASCII characters
let blah = "\u03c0\u042f\u97f3\u00e6\u221e"; let blah = "\u{3c0}\u{42f}\u{97f3}\u{e6}\u{221e}";
let child_name = "child"; let child_name = "child";
let child_dir = format!("process-spawn-with-unicode-params-{}", blah); let child_dir = format!("process-spawn-with-unicode-params-{}", blah);
// parameters sent to child / expected to be received from parent // parameters sent to child / expected to be received from parent
let arg = blah; let arg = blah;
let cwd = my_dir.join(Path::new(child_dir.clone())); let cwd = my_dir.join(&child_dir);
let env = ("RUST_TEST_PROC_SPAWN_UNICODE".to_string(), blah.to_string()); let env = ("RUST_TEST_PROC_SPAWN_UNICODE".to_string(), blah.to_string());
// am I the parent or the child? // am I the parent or the child?
@ -47,24 +48,22 @@ fn main() {
let child_filestem = Path::new(child_name); let child_filestem = Path::new(child_name);
let child_filename = child_filestem.with_extension(my_ext); let child_filename = child_filestem.with_extension(my_ext);
let child_path = cwd.join(child_filename); let child_path = cwd.join(&child_filename);
// make a separate directory for the child // make a separate directory for the child
drop(fs::mkdir(&cwd, old_io::USER_RWX).is_ok()); let _ = fs::create_dir(&cwd);
assert!(fs::copy(&my_path, &child_path).is_ok()); fs::copy(&my_path, &child_path).unwrap();
let mut my_env = my_env;
my_env.push(env);
// run child // run child
let p = Command::new(&child_path) let p = Command::new(&child_path)
.arg(arg) .arg(arg)
.cwd(&cwd) .current_dir(&cwd)
.env_set_all(&my_env) .env(&env.0, &env.1)
.spawn().unwrap().wait_with_output().unwrap(); .spawn().unwrap().wait_with_output().unwrap();
// display the output // display the output
assert!(old_io::stdout().write(&p.output).is_ok()); io::stdout().write_all(&p.stdout).unwrap();
assert!(old_io::stderr().write(&p.error).is_ok()); io::stderr().write_all(&p.stderr).unwrap();
// make sure the child succeeded // make sure the child succeeded
assert!(p.status.success()); assert!(p.status.success());
@ -72,7 +71,7 @@ fn main() {
} else { // child } else { // child
// check working directory (don't try to compare with `cwd` here!) // check working directory (don't try to compare with `cwd` here!)
assert!(my_cwd.ends_with_path(&Path::new(child_dir))); assert!(my_cwd.ends_with(&child_dir));
// check arguments // check arguments
assert_eq!(&*my_args[1], arg); assert_eq!(&*my_args[1], arg);