From 94d71f8836b3bfac3370e4d324ca1987d843552f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 24 Feb 2015 23:27:20 -0800 Subject: [PATCH] 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. --- src/liballoc/arc.rs | 23 +- src/libstd/io/lazy.rs | 59 ++++ src/libstd/io/mod.rs | 5 + src/libstd/io/stdio.rs | 325 ++++++++++++++++++ src/libstd/old_io/stdio.rs | 8 +- src/libstd/sys/unix/mod.rs | 1 + src/libstd/sys/unix/stdio.rs | 52 +++ src/libstd/sys/windows/c.rs | 6 + src/libstd/sys/windows/handle.rs | 11 +- src/libstd/sys/windows/mod.rs | 1 + src/libstd/sys/windows/stdio.rs | 155 +++++++++ src/test/bench/shootout-k-nucleotide.rs | 40 ++- src/test/bench/sudoku.rs | 50 +-- src/test/run-pass-valgrind/cleanup-stdin.rs | 1 + src/test/run-pass/issue-13304.rs | 20 +- src/test/run-pass/issue-14456.rs | 24 +- src/test/run-pass/issue-16671.rs | 8 +- src/test/run-pass/issue-17322.rs | 8 +- src/test/run-pass/issue-4333.rs | 4 +- .../process-spawn-with-unicode-params.rs | 39 +-- 20 files changed, 732 insertions(+), 108 deletions(-) create mode 100644 src/libstd/io/lazy.rs create mode 100644 src/libstd/io/stdio.rs create mode 100644 src/libstd/sys/unix/stdio.rs create mode 100644 src/libstd/sys/windows/stdio.rs diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 88a3752c88a1..c95b413b397e 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -201,10 +201,11 @@ impl Arc { impl Arc { #[inline] fn inner(&self) -> &ArcInner { - // This unsafety is ok because while this arc is alive we're guaranteed that the inner - // pointer is valid. Furthermore, we know that the `ArcInner` structure itself is `Sync` - // because the inner data is `Sync` as well, so we're ok loaning out an immutable pointer - // to these contents. + // This unsafety is ok because while this arc is alive we're guaranteed + // that the inner pointer is valid. Furthermore, we know that the + // `ArcInner` structure itself is `Sync` because the inner data is + // `Sync` as well, so we're ok loaning out an immutable pointer to these + // contents. unsafe { &**self._ptr } } } @@ -236,13 +237,15 @@ impl Clone for Arc { /// ``` #[inline] fn clone(&self) -> Arc { - // Using a relaxed ordering is alright here, as knowledge of the original reference - // prevents other threads from erroneously deleting the object. + // Using a relaxed ordering is alright here, as knowledge of the + // original reference prevents other threads from erroneously deleting + // the object. // - // As explained in the [Boost documentation][1], Increasing the reference counter can - // always be done with memory_order_relaxed: New references to an object can only be formed - // from an existing reference, and passing an existing reference from one thread to another - // must already provide any required synchronization. + // As explained in the [Boost documentation][1], Increasing the + // reference counter can always be done with memory_order_relaxed: New + // references to an object can only be formed from an existing + // 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) self.inner().strong.fetch_add(1, Relaxed); diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs new file mode 100644 index 000000000000..c9b105f72a53 --- /dev/null +++ b/src/libstd/io/lazy.rs @@ -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 or the MIT license +// , 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 { + pub lock: StaticMutex, + pub ptr: UnsafeCell<*mut Arc>, + pub init: fn() -> Arc, +} + +unsafe impl Sync for Lazy {} + +macro_rules! lazy_init { + ($init:expr) => (::io::lazy::Lazy { + lock: ::sync::MUTEX_INIT, + ptr: ::cell::UnsafeCell { value: 0 as *mut _ }, + init: $init, + }) +} + +impl Lazy { + pub fn get(&'static self) -> Option> { + 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> { + 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)()) + } +} diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 3b4e15953c46..5510c0203e6c 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -39,6 +39,10 @@ pub use self::buffered::IntoInnerError; pub use self::cursor::Cursor; pub use self::error::{Result, Error, ErrorKind}; 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; mod buffered; @@ -46,6 +50,7 @@ mod cursor; mod error; mod impls; mod util; +mod stdio; const DEFAULT_BUF_SIZE: usize = 64 * 1024; diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs new file mode 100644 index 000000000000..61ad9905771a --- /dev/null +++ b/src/libstd/io/stdio.rs @@ -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 or the MIT license +// , 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 { self.0.read(buf) } +} +impl Write for StdoutRaw { + fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } + fn flush(&mut self) -> io::Result<()> { Ok(()) } +} +impl Write for StderrRaw { + fn write(&mut self, buf: &[u8]) -> io::Result { 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>>, +} + +/// 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>, +} + +/// 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>> = lazy_init!(stdin_init); + return Stdin { + inner: INSTANCE.get().expect("cannot access stdin during shutdown"), + }; + + fn stdin_init() -> Arc>> { + // 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 { + self.lock().read(buf) + } + + fn read_to_end(&mut self, buf: &mut Vec) -> 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 { + // 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>>, +} + +/// 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>, +} + +/// 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>> = lazy_init!(stdout_init); + return Stdout { + inner: INSTANCE.get().expect("cannot access stdout during shutdown"), + }; + + fn stdout_init() -> Arc>> { + 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 { + 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 { + 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>, +} + +/// 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> = lazy_init!(stderr_init); + return Stderr { + inner: INSTANCE.get().expect("cannot access stderr during shutdown"), + }; + + fn stderr_init() -> Arc> { + 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 { + 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 { + self.inner.write(&buf[..cmp::min(buf.len(), OUT_MAX)]) + } + fn flush(&mut self) -> io::Result<()> { self.inner.flush() } +} diff --git a/src/libstd/old_io/stdio.rs b/src/libstd/old_io/stdio.rs index 56a707c24a6c..a5df21749e22 100644 --- a/src/libstd/old_io/stdio.rs +++ b/src/libstd/old_io/stdio.rs @@ -224,10 +224,10 @@ pub fn stdin() -> StdinReader { unsafe { ONCE.call_once(|| { - // 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. + // 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. let stdin = if cfg!(windows) { BufferedReader::with_capacity(8 * 1024, stdin_raw()) } else { diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 632270bc5ccb..80bfd57e933c 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -63,6 +63,7 @@ pub mod time; pub mod timer; pub mod tty; pub mod udp; +pub mod stdio; pub mod addrinfo { pub use sys_common::net::get_host_addresses; diff --git a/src/libstd/sys/unix/stdio.rs b/src/libstd/sys/unix/stdio.rs new file mode 100644 index 000000000000..2f9610fa5b5a --- /dev/null +++ b/src/libstd/sys/unix/stdio.rs @@ -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 or the MIT license +// , 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 { + 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 { + 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 { + let fd = FileDesc::new(libc::STDERR_FILENO); + let ret = fd.write(data); + fd.into_raw(); + return ret; + } +} diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index 2d1a5e10bd63..8ed7302b6653 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -48,6 +48,11 @@ pub const WSAESHUTDOWN: libc::c_int = 10058; pub const ERROR_NO_MORE_FILES: libc::DWORD = 18; 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)] #[cfg(target_arch = "x86")] pub struct WSADATA { @@ -427,6 +432,7 @@ extern "system" { DesiredAccess: libc::DWORD, TokenHandle: *mut libc::HANDLE) -> libc::BOOL; pub fn GetCurrentProcess() -> libc::HANDLE; + pub fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE; } #[link(name = "userenv")] diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index 99de659be41e..0089dcad455d 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -10,9 +10,10 @@ use prelude::v1::*; -use libc::{self, HANDLE}; -use io; use io::ErrorKind; +use io; +use libc::{self, HANDLE}; +use mem; use ptr; use sys::cvt; @@ -28,6 +29,12 @@ impl Handle { 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 { read(self.0, buf) } diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 5bb2a134533e..3bdadbb9012e 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -61,6 +61,7 @@ pub mod time; pub mod timer; pub mod tty; pub mod udp; +pub mod stdio; pub mod addrinfo { pub use sys_common::net::get_host_addresses; diff --git a/src/libstd/sys/windows/stdio.rs b/src/libstd/sys/windows/stdio.rs new file mode 100644 index 000000000000..72ce8b7c6e30 --- /dev/null +++ b/src/libstd/sys/windows/stdio.rs @@ -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 or the MIT license +// , 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); + +enum Output { + Console(NoClose), + Pipe(NoClose), +} + +pub struct Stdin { + handle: Output, + utf8: Mutex>>, +} +pub struct Stdout(Output); +pub struct Stderr(Output); + +fn get(handle: libc::DWORD) -> io::Result { + 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 { + 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::>(), + 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 { + 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 = 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 { + 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 { + 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) +} diff --git a/src/test/bench/shootout-k-nucleotide.rs b/src/test/bench/shootout-k-nucleotide.rs index fb75c67253c6..f239a0d78d1d 100644 --- a/src/test/bench/shootout-k-nucleotide.rs +++ b/src/test/bench/shootout-k-nucleotide.rs @@ -43,12 +43,16 @@ #![feature(box_syntax)] use std::ascii::OwnedAsciiExt; +use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::io; use std::slice; use std::sync::Arc; use std::thread; 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] = [ "GGT", @@ -73,7 +77,7 @@ impl Code { 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)) } @@ -81,7 +85,7 @@ impl Code { 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 result = Vec::new(); for _ in 0..frame { @@ -113,13 +117,13 @@ struct PrintCallback(&'static str); impl TableCallback for PrintCallback { fn f(&self, entry: &mut Entry) { let PrintCallback(s) = *self; - println!("{}\t{}", entry.count as int, s); + println!("{}\t{}", entry.count, s); } } struct Entry { code: Code, - count: uint, + count: usize, next: Option>, } @@ -165,20 +169,20 @@ impl Table { 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 { code: key, count: 0, next: None, }; c.f(&mut *entry); - self.items[index as uint] = Some(entry); + self.items[index as usize] = Some(entry); 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 { c.f(&mut **entry); return; @@ -233,10 +237,10 @@ fn pack_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(); if input.len() < frame { return frequencies; } let mut code = Code(0); @@ -256,7 +260,7 @@ fn generate_frequencies(mut input: &[u8], frame: uint) -> Table { frequencies } -fn print_frequencies(frequencies: &Table, frame: uint) { +fn print_frequencies(frequencies: &Table, frame: usize) { let mut vector = Vec::new(); for entry in frequencies.iter() { 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)) } -fn get_sequence(r: &mut R, key: &str) -> Vec { +fn get_sequence(r: &mut R, key: &str) -> Vec { 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) { res.push_all(l.trim().as_bytes()); @@ -291,13 +295,13 @@ fn get_sequence(r: &mut R, key: &str) -> Vec { } fn main() { - let input = if std::env::var_os("RUST_BENCH").is_some() { - let fd = std::old_io::File::open(&Path::new("shootout-k-nucleotide.data")); - get_sequence(&mut std::old_io::BufferedReader::new(fd), ">THREE") + let input = if env::var_os("RUST_BENCH").is_some() { + let f = File::open("shootout-k-nucleotide.data").unwrap(); + get_sequence(&mut io::BufReader::new(f), ">THREE") } else { - let mut stdin = std::old_io::stdin(); + let stdin = io::stdin(); let mut stdin = stdin.lock(); - get_sequence(&mut *stdin, ">THREE") + get_sequence(&mut stdin, ">THREE") }; let input = Arc::new(input); diff --git a/src/test/bench/sudoku.rs b/src/test/bench/sudoku.rs index b45f241e8e5d..40e1e7d2b76b 100644 --- a/src/test/bench/sudoku.rs +++ b/src/test/bench/sudoku.rs @@ -13,9 +13,8 @@ #![feature(box_syntax)] #![allow(non_snake_case)] -use std::old_io::BufferedReader; -use std::old_io::stdio::StdReader; -use std::old_io; +use std::io::prelude::*; +use std::io; use std::iter::repeat; use std::num::Int; use std::env; @@ -37,7 +36,7 @@ use std::env; // // internal type of sudoku grids -type grid = Vec > ; +type grid = Vec>; struct Sudoku { grid: grid @@ -55,9 +54,11 @@ impl Sudoku { return Sudoku::new(g) } - pub fn read(mut reader: &mut BufferedReader) -> Sudoku { + pub fn read(reader: &mut BufRead) -> Sudoku { /* 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]) .take(10).collect::>(); @@ -71,7 +72,7 @@ impl Sudoku { if comps.len() == 3 { let row = comps[0].parse::().unwrap(); let col = comps[1].parse::().unwrap(); - g[row as uint][col as uint] = comps[2].parse().unwrap(); + g[row as usize][col as usize] = comps[2].parse().unwrap(); } else { panic!("Invalid sudoku file"); @@ -80,11 +81,11 @@ impl Sudoku { 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 { - write!(writer, "{}", self.grid[row as uint][0]); + write!(writer, "{}", self.grid[row as usize][0]); 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"); } @@ -95,7 +96,7 @@ impl Sudoku { let mut work: Vec<(u8, u8)> = Vec::new(); /* queue of uncolored fields */ for row 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 { work.push((row, col)); } @@ -107,7 +108,7 @@ impl Sudoku { while ptr < end { let (row, col) = work[ptr]; // 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); if self.next_color(row, col, the_color) { // yes: advance work list @@ -130,10 +131,10 @@ impl Sudoku { // find first remaining color that is available 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; } - self.grid[row as uint][col as uint] = 0u8; + self.grid[row as usize][col as usize] = 0u8; return false; } @@ -141,9 +142,9 @@ impl Sudoku { fn drop_colors(&mut self, avail: &mut Colors, row: u8, col: u8) { for idx in 0u8..9u8 { /* 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 */ - avail.remove(self.grid[row as uint][idx as uint]); + avail.remove(self.grid[row as usize][idx as usize]); } // check same block fields @@ -151,7 +152,7 @@ impl Sudoku { let col0 = (col / 3u8) * 3u8; for alt_row in row0..row0 + 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 { fn new(start_color: u8) -> Colors { // Sets bits 9..start_color - let tails = !0u16 << start_color as uint; + let tails = !0u16 << start_color as usize; return Colors(HEADS & tails); } @@ -182,7 +183,7 @@ impl Colors { fn remove(&mut self, color: u8) { if color != 0u8 { let Colors(val) = *self; - let mask = !(1u16 << color as uint); + let mask = !(1u16 << color as usize); *self = Colors(val & mask); } } @@ -269,15 +270,16 @@ fn check_DEFAULT_SUDOKU_solution() { } fn main() { - let args = env::args(); + let args = env::args(); let use_default = args.len() == 1; let mut sudoku = if use_default { Sudoku::from_vec(&DEFAULT_SUDOKU) } else { - let mut stdin = old_io::stdin(); - let mut stdin = stdin.lock(); - Sudoku::read(&mut *stdin) + let stdin = io::stdin(); + let mut locked = stdin.lock(); + Sudoku::read(&mut locked) }; sudoku.solve(); - sudoku.write(&mut old_io::stdout()); + let out = io::stdout(); + sudoku.write(&mut out.lock()); } diff --git a/src/test/run-pass-valgrind/cleanup-stdin.rs b/src/test/run-pass-valgrind/cleanup-stdin.rs index c16f1f4c842a..127be1f90d5f 100644 --- a/src/test/run-pass-valgrind/cleanup-stdin.rs +++ b/src/test/run-pass-valgrind/cleanup-stdin.rs @@ -10,4 +10,5 @@ fn main() { let _ = std::old_io::stdin(); + let _ = std::io::stdin(); } diff --git a/src/test/run-pass/issue-13304.rs b/src/test/run-pass/issue-13304.rs index 4a7d6be55a16..f1c747eca684 100644 --- a/src/test/run-pass/issue-13304.rs +++ b/src/test/run-pass/issue-13304.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// ignore-fast - use std::env; -use std::old_io; +use std::io::prelude::*; +use std::io; +use std::process::{Command, Stdio}; use std::str; fn main() { @@ -25,17 +25,19 @@ fn main() { fn parent() { let args: Vec = env::args().collect(); - let mut p = old_io::process::Command::new(&args[0]) - .arg("child").spawn().unwrap(); - p.stdin.as_mut().unwrap().write_str("test1\ntest2\ntest3").unwrap(); + let mut p = Command::new(&args[0]).arg("child") + .stdout(Stdio::capture()) + .stdin(Stdio::capture()) + .spawn().unwrap(); + p.stdin.as_mut().unwrap().write_all(b"test1\ntest2\ntest3").unwrap(); let out = p.wait_with_output().unwrap(); assert!(out.status.success()); - let s = str::from_utf8(&out.output).unwrap(); - assert_eq!(s, "test1\n\ntest2\n\ntest3\n"); + let s = str::from_utf8(&out.stdout).unwrap(); + assert_eq!(s, "test1\ntest2\ntest3\n"); } fn child() { - let mut stdin = old_io::stdin(); + let mut stdin = io::stdin(); for line in stdin.lock().lines() { println!("{}", line.unwrap()); } diff --git a/src/test/run-pass/issue-14456.rs b/src/test/run-pass/issue-14456.rs index 723db9485ca6..7e4c464d9aab 100644 --- a/src/test/run-pass/issue-14456.rs +++ b/src/test/run-pass/issue-14456.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -use std::old_io::process; -use std::old_io::Command; -use std::old_io; use std::env; +use std::io::prelude::*; +use std::io; +use std::process::{Command, Stdio}; fn main() { let args: Vec = env::args().collect(); @@ -21,22 +20,23 @@ fn main() { } test(); - } fn child() { - old_io::stdout().write_line("foo").unwrap(); - old_io::stderr().write_line("bar").unwrap(); - let mut stdin = old_io::stdin(); - assert_eq!(stdin.lock().read_line().err().unwrap().kind, old_io::EndOfFile); + writeln!(&mut io::stdout(), "foo").unwrap(); + writeln!(&mut io::stderr(), "bar").unwrap(); + let mut stdin = io::stdin(); + let mut s = String::new(); + stdin.lock().read_line(&mut s).unwrap(); + assert_eq!(s.len(), 0); } fn test() { let args: Vec = env::args().collect(); let mut p = Command::new(&args[0]).arg("child") - .stdin(process::Ignored) - .stdout(process::Ignored) - .stderr(process::Ignored) + .stdin(Stdio::capture()) + .stdout(Stdio::capture()) + .stderr(Stdio::capture()) .spawn().unwrap(); assert!(p.wait().unwrap().success()); } diff --git a/src/test/run-pass/issue-16671.rs b/src/test/run-pass/issue-16671.rs index b06c4923c16c..6e4838be5296 100644 --- a/src/test/run-pass/issue-16671.rs +++ b/src/test/run-pass/issue-16671.rs @@ -17,11 +17,13 @@ // A var moved into a proc, that has a mutable loan path should // not trigger a misleading unused_mut warning. +use std::io::prelude::*; use std::thread; pub fn main() { - let mut stdin = std::old_io::stdin(); + let mut stdin = std::io::stdin(); thread::spawn(move|| { - let _ = stdin.read_to_end(); - }); + let mut v = Vec::new(); + let _ = stdin.read_to_end(&mut v); + }).join().ok().unwrap(); } diff --git a/src/test/run-pass/issue-17322.rs b/src/test/run-pass/issue-17322.rs index dd1cfb5e3428..d4c32f42188b 100644 --- a/src/test/run-pass/issue-17322.rs +++ b/src/test/run-pass/issue-17322.rs @@ -11,13 +11,13 @@ #![allow(unknown_features)] #![feature(box_syntax)] -use std::old_io; +use std::io::{self, Write}; -fn f(wr: &mut Writer) { - wr.write_str("hello").ok().expect("failed"); +fn f(wr: &mut Write) { + wr.write_all(b"hello").ok().expect("failed"); } fn main() { - let mut wr = box old_io::stdout() as Box; + let mut wr = box io::stdout() as Box; f(&mut wr); } diff --git a/src/test/run-pass/issue-4333.rs b/src/test/run-pass/issue-4333.rs index 28ab3c3ef125..074bbf270fd7 100644 --- a/src/test/run-pass/issue-4333.rs +++ b/src/test/run-pass/issue-4333.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::old_io; +use std::io; 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!"); } diff --git a/src/test/run-pass/process-spawn-with-unicode-params.rs b/src/test/run-pass/process-spawn-with-unicode-params.rs index 017784990f41..3e5f84fa26fe 100644 --- a/src/test/run-pass/process-spawn-with-unicode-params.rs +++ b/src/test/run-pass/process-spawn-with-unicode-params.rs @@ -16,30 +16,31 @@ // non-ASCII characters. The child process ensures all the strings are // intact. -use std::old_io; -use std::old_io::fs; -use std::old_io::Command; +use std::io::prelude::*; +use std::io; +use std::fs; +use std::process::Command; use std::os; use std::env; -use std::old_path::Path; +use std::path::{Path, PathBuf}; fn main() { let my_args = env::args().collect::>(); - let my_cwd = os::getcwd().unwrap(); + let my_cwd = PathBuf::new(os::getcwd().unwrap().as_str().unwrap()); let my_env = env::vars().collect::>(); - let my_path = Path::new(os::self_exe_name().unwrap()); - let my_dir = my_path.dir_path(); - let my_ext = my_path.extension_str().unwrap_or(""); + let my_path = PathBuf::new(os::self_exe_name().unwrap().as_str().unwrap()); + let my_dir = my_path.parent().unwrap(); + let my_ext = my_path.extension().and_then(|s| s.to_str()).unwrap_or(""); // 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_dir = format!("process-spawn-with-unicode-params-{}", blah); // parameters sent to child / expected to be received from parent 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()); // am I the parent or the child? @@ -47,24 +48,22 @@ fn main() { let child_filestem = Path::new(child_name); 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 - drop(fs::mkdir(&cwd, old_io::USER_RWX).is_ok()); - assert!(fs::copy(&my_path, &child_path).is_ok()); - let mut my_env = my_env; - my_env.push(env); + let _ = fs::create_dir(&cwd); + fs::copy(&my_path, &child_path).unwrap(); // run child let p = Command::new(&child_path) .arg(arg) - .cwd(&cwd) - .env_set_all(&my_env) + .current_dir(&cwd) + .env(&env.0, &env.1) .spawn().unwrap().wait_with_output().unwrap(); // display the output - assert!(old_io::stdout().write(&p.output).is_ok()); - assert!(old_io::stderr().write(&p.error).is_ok()); + io::stdout().write_all(&p.stdout).unwrap(); + io::stderr().write_all(&p.stderr).unwrap(); // make sure the child succeeded assert!(p.status.success()); @@ -72,7 +71,7 @@ fn main() { } else { // child // 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 assert_eq!(&*my_args[1], arg);