From e423fcf0e0166da55f88233e0be5eacba55bc0bc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Dec 2014 10:59:41 -0800 Subject: std: Enforce Unicode in fmt::Writer This commit is an implementation of [RFC 526][rfc] which is a change to alter the definition of the old `fmt::FormatWriter`. The new trait, renamed to `Writer`, now only exposes one method `write_str` in order to guarantee that all implementations of the formatting traits can only produce valid Unicode. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md One of the primary improvements of this patch is the performance of the `.to_string()` method by avoiding an almost-always redundant UTF-8 check. This is a breaking change due to the renaming of the trait as well as the loss of the `write` method, but migration paths should be relatively easy: * All usage of `write` should move to `write_str`. If truly binary data was being written in an implementation of `Show`, then it will need to use a different trait or an altogether different code path. * All usage of `write!` should continue to work as-is with no modifications. * All usage of `Show` where implementations just delegate to another should continue to work as-is. [breaking-change] Closes #20352 --- src/libstd/failure.rs | 6 +----- src/libstd/fmt.rs | 23 +++++------------------ src/libstd/io/mod.rs | 8 ++++---- src/libstd/rt/unwind.rs | 19 ++++--------------- src/libstd/rt/util.rs | 18 ++++++++++++------ 5 files changed, 26 insertions(+), 48 deletions(-) (limited to 'src/libstd') diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index 7010eae6dba..30dc48f6fcc 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -14,7 +14,6 @@ use prelude::*; use any::{Any, AnyRefExt}; use cell::RefCell; -use fmt; use io::IoResult; use rt::{backtrace, unwind}; use rt::util::{Stderr, Stdio}; @@ -29,10 +28,7 @@ thread_local! { impl Writer for Stdio { fn write(&mut self, bytes: &[u8]) -> IoResult<()> { - fn fmt_write(f: &mut F, bytes: &[u8]) { - let _ = f.write(bytes); - } - fmt_write(self, bytes); + let _ = self.write_bytes(bytes); Ok(()) } } diff --git a/src/libstd/fmt.rs b/src/libstd/fmt.rs index 957dd54a037..32f5f2d4536 100644 --- a/src/libstd/fmt.rs +++ b/src/libstd/fmt.rs @@ -201,7 +201,7 @@ //! // for details, and the function `pad` can be used to pad strings. //! let decimals = f.precision().unwrap_or(3); //! let string = f64::to_str_exact(magnitude, decimals); -//! f.pad_integral(true, "", string.as_bytes()) +//! f.pad_integral(true, "", string.as_slice()) //! } //! } //! @@ -390,13 +390,9 @@ #![experimental] -use io::Writer; -use io; -use result::Result::{Ok, Err}; use string; -use vec::Vec; -pub use core::fmt::{Formatter, Result, FormatWriter, rt}; +pub use core::fmt::{Formatter, Result, Writer, rt}; pub use core::fmt::{Show, Octal, Binary}; pub use core::fmt::{LowerHex, UpperHex, Pointer}; pub use core::fmt::{LowerExp, UpperExp}; @@ -424,16 +420,7 @@ pub use core::fmt::{argument, argumentuint}; #[experimental = "this is an implementation detail of format! and should not \ be called directly"] pub fn format(args: Arguments) -> string::String { - let mut output = Vec::new(); - let _ = write!(&mut output as &mut Writer, "{}", args); - string::String::from_utf8(output).unwrap() -} - -impl<'a> Writer for Formatter<'a> { - fn write(&mut self, b: &[u8]) -> io::IoResult<()> { - match (*self).write(b) { - Ok(()) => Ok(()), - Err(Error) => Err(io::standard_error(io::OtherIoError)) - } - } + let mut output = string::String::new(); + let _ = write!(&mut output, "{}", args); + output } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index e8b852ee492..3f6eb217245 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1028,16 +1028,16 @@ pub trait Writer { /// /// This function will return any I/O error reported while formatting. fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> { - // Create a shim which translates a Writer to a FormatWriter and saves + // Create a shim which translates a Writer to a fmt::Writer and saves // off I/O errors. instead of discarding them struct Adaptor<'a, T:'a> { inner: &'a mut T, error: IoResult<()>, } - impl<'a, T: Writer> fmt::FormatWriter for Adaptor<'a, T> { - fn write(&mut self, bytes: &[u8]) -> fmt::Result { - match self.inner.write(bytes) { + impl<'a, T: Writer> fmt::Writer for Adaptor<'a, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write(s.as_bytes()) { Ok(()) => Ok(()), Err(e) => { self.error = Err(e); diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index e0c512706e6..8d7713d0558 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -493,27 +493,16 @@ pub extern fn rust_begin_unwind(msg: fmt::Arguments, /// the actual formatting into this shared place. #[inline(never)] #[cold] pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, uint)) -> ! { - use fmt::FormatWriter; + use fmt::Writer; // We do two allocations here, unfortunately. But (a) they're // required with the current scheme, and (b) we don't handle // panic + OOM properly anyway (see comment in begin_unwind // below). - struct VecWriter<'a> { v: &'a mut Vec } - - impl<'a> fmt::FormatWriter for VecWriter<'a> { - fn write(&mut self, buf: &[u8]) -> fmt::Result { - self.v.push_all(buf); - Ok(()) - } - } - - let mut v = Vec::new(); - let _ = write!(&mut VecWriter { v: &mut v }, "{}", msg); - - let msg = box String::from_utf8_lossy(v.as_slice()).into_owned(); - begin_unwind_inner(msg, file_line) + let mut s = String::new(); + let _ = write!(&mut s, "{}", msg); + begin_unwind_inner(box s, file_line) } /// This is the entry point of unwinding for panic!() and assert!(). diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index fee86e33455..fa7c305d69e 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -96,8 +96,8 @@ pub const Stdout: Stdio = Stdio(libc::STDOUT_FILENO); #[allow(non_upper_case_globals)] pub const Stderr: Stdio = Stdio(libc::STDERR_FILENO); -impl fmt::FormatWriter for Stdio { - fn write(&mut self, data: &[u8]) -> fmt::Result { +impl Stdio { + pub fn write_bytes(&mut self, data: &[u8]) { #[cfg(unix)] type WriteLen = libc::size_t; #[cfg(windows)] @@ -108,6 +108,12 @@ impl fmt::FormatWriter for Stdio { data.as_ptr() as *const libc::c_void, data.len() as WriteLen); } + } +} + +impl fmt::Writer for Stdio { + fn write_str(&mut self, data: &str) -> fmt::Result { + self.write_bytes(data.as_bytes()); Ok(()) // yes, we're lying } } @@ -117,16 +123,16 @@ pub fn dumb_print(args: fmt::Arguments) { } pub fn abort(args: fmt::Arguments) -> ! { - use fmt::FormatWriter; + use fmt::Writer; struct BufWriter<'a> { buf: &'a mut [u8], pos: uint, } - impl<'a> FormatWriter for BufWriter<'a> { - fn write(&mut self, bytes: &[u8]) -> fmt::Result { + impl<'a> fmt::Writer for BufWriter<'a> { + fn write_str(&mut self, bytes: &str) -> fmt::Result { let left = self.buf.slice_from_mut(self.pos); - let to_write = bytes[..cmp::min(bytes.len(), left.len())]; + let to_write = bytes.as_bytes()[..cmp::min(bytes.len(), left.len())]; slice::bytes::copy_memory(left, to_write); self.pos += to_write.len(); Ok(()) -- cgit 1.4.1-3-g733a5