Auto merge of #32184 - ollie27:win_stdout, r=alexcrichton

Fixup stout/stderr on Windows

WriteConsoleW can fail if called with a large buffer so we need to slice
any stdout/stderr output.
However the current slicing has a few problems:
 1. It slices by byte but still expects valid UTF-8.
 2. The slicing happens even when not outputting to a console.
 3. panic! output is not sliced.

This fixes these issues by moving the slice to right before
WriteConsoleW and slicing on a char boundary.
This commit is contained in:
bors 2016-03-13 09:27:17 -07:00
commit ce943eb369
2 changed files with 21 additions and 23 deletions

View File

@ -12,7 +12,6 @@ use prelude::v1::*;
use io::prelude::*;
use cell::{RefCell, BorrowState};
use cmp;
use fmt;
use io::lazy::Lazy;
use io::{self, BufReader, LineWriter};
@ -312,22 +311,6 @@ impl<'a> BufRead for StdinLock<'a> {
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
@ -440,7 +423,7 @@ impl Write for Stdout {
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Write for StdoutLock<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
self.inner.borrow_mut().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.borrow_mut().flush()
@ -546,7 +529,7 @@ impl Write for Stderr {
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Write for StderrLock<'a> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.borrow_mut().write(&buf[..cmp::min(buf.len(), OUT_MAX)])
self.inner.borrow_mut().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.borrow_mut().flush()

View File

@ -13,6 +13,7 @@
use prelude::v1::*;
use io::prelude::*;
use cmp;
use io::{self, Cursor};
use ptr;
use str;
@ -58,10 +59,24 @@ fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
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.encode_utf16().collect::<Vec<u16>>(),
None => return Err(invalid_encoding()),
// 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.
//
// For some other references, it appears that this problem has been
// encountered by others [1] [2]. We choose the number 8K 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
const OUT_MAX: usize = 8192;
let len = cmp::min(data.len(), OUT_MAX);
let utf8 = match str::from_utf8(&data[..len]) {
Ok(s) => s,
Err(ref e) if e.valid_up_to() == 0 => return Err(invalid_encoding()),
Err(e) => str::from_utf8(&data[..e.valid_up_to()]).unwrap(),
};
let utf16 = utf8.encode_utf16().collect::<Vec<u16>>();
let mut written = 0;
try!(cvt(unsafe {
c::WriteConsoleW(handle,
@ -74,7 +89,7 @@ fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
// 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())
Ok(utf8.len())
}
impl Stdin {