about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-12-12 10:59:41 -0800
committerAlex Crichton <alex@alexcrichton.com>2015-01-01 22:04:46 -0800
commite423fcf0e0166da55f88233e0be5eacba55bc0bc (patch)
tree0c4a4ffadfa870dcf23e1d55b224a1d7c2805f2a /src/libcore
parentcd614164e692cca3a1460737f581fcb6d4630baf (diff)
downloadrust-e423fcf0e0166da55f88233e0be5eacba55bc0bc.tar.gz
rust-e423fcf0e0166da55f88233e0be5eacba55bc0bc.zip
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
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/fmt/float.rs20
-rw-r--r--src/libcore/fmt/mod.rs71
-rw-r--r--src/libcore/fmt/num.rs4
3 files changed, 46 insertions, 49 deletions
diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs
index e1728d762ed..a39168ec1ec 100644
--- a/src/libcore/fmt/float.rs
+++ b/src/libcore/fmt/float.rs
@@ -23,7 +23,7 @@ use num::FpCategory as Fp;
 use ops::FnOnce;
 use result::Result::Ok;
 use slice::{mod, SliceExt};
-use str::StrExt;
+use str::{mod, StrExt};
 
 /// A flag that specifies whether to use exponential (scientific) notation.
 pub enum ExponentFormat {
@@ -95,7 +95,7 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
     exp_upper: bool,
     f: F
 ) -> U where
-    F: FnOnce(&[u8]) -> U,
+    F: FnOnce(&str) -> U,
 {
     assert!(2 <= radix && radix <= 36);
     match exp_format {
@@ -109,12 +109,12 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
     let _1: T = Float::one();
 
     match num.classify() {
-        Fp::Nan => return f("NaN".as_bytes()),
+        Fp::Nan => return f("NaN"),
         Fp::Infinite if num > _0 => {
-            return f("inf".as_bytes());
+            return f("inf");
         }
         Fp::Infinite if num < _0 => {
-            return f("-inf".as_bytes());
+            return f("-inf");
         }
         _ => {}
     }
@@ -314,11 +314,11 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
                 end: &'a mut uint,
             }
 
-            impl<'a> fmt::FormatWriter for Filler<'a> {
-                fn write(&mut self, bytes: &[u8]) -> fmt::Result {
+            impl<'a> fmt::Writer for Filler<'a> {
+                fn write_str(&mut self, s: &str) -> fmt::Result {
                     slice::bytes::copy_memory(self.buf.slice_from_mut(*self.end),
-                                              bytes);
-                    *self.end += bytes.len();
+                                              s.as_bytes());
+                    *self.end += s.len();
                     Ok(())
                 }
             }
@@ -332,5 +332,5 @@ pub fn float_to_str_bytes_common<T: Float, U, F>(
         }
     }
 
-    f(buf[..end])
+    f(unsafe { str::from_utf8_unchecked(buf[..end]) })
 }
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs
index 87fcb12e29f..f2439d515b4 100644
--- a/src/libcore/fmt/mod.rs
+++ b/src/libcore/fmt/mod.rs
@@ -24,7 +24,7 @@ use result::Result::{Ok, Err};
 use result;
 use slice::SliceExt;
 use slice;
-use str::{StrExt, Utf8Error};
+use str::{mod, StrExt, Utf8Error};
 
 pub use self::num::radix;
 pub use self::num::Radix;
@@ -57,7 +57,7 @@ pub struct Error;
 /// library. The `write!` macro accepts an instance of `io::Writer`, and the
 /// `io::Writer` trait is favored over implementing this trait.
 #[experimental = "waiting for core and I/O reconciliation"]
-pub trait FormatWriter {
+pub trait Writer {
     /// Writes a slice of bytes into this writer, returning whether the write
     /// succeeded.
     ///
@@ -68,7 +68,7 @@ pub trait FormatWriter {
     /// # Errors
     ///
     /// This function will return an instance of `FormatError` on error.
-    fn write(&mut self, bytes: &[u8]) -> Result;
+    fn write_str(&mut self, s: &str) -> Result;
 
     /// Glue for usage of the `write!` macro with implementers of this trait.
     ///
@@ -88,7 +88,7 @@ pub struct Formatter<'a> {
     width: Option<uint>,
     precision: Option<uint>,
 
-    buf: &'a mut (FormatWriter+'a),
+    buf: &'a mut (Writer+'a),
     curarg: slice::Iter<'a, Argument<'a>>,
     args: &'a [Argument<'a>],
 }
@@ -258,17 +258,6 @@ pub trait UpperExp for Sized? {
     fn fmt(&self, &mut Formatter) -> Result;
 }
 
-static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
-    position: rt::ArgumentNext,
-    format: rt::FormatSpec {
-        fill: ' ',
-        align: rt::AlignUnknown,
-        flags: 0,
-        precision: rt::CountImplied,
-        width: rt::CountImplied,
-    }
-};
-
 /// The `write` function takes an output stream, a precompiled format string,
 /// and a list of arguments. The arguments will be formatted according to the
 /// specified format string into the output stream provided.
@@ -279,7 +268,7 @@ static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
 ///   * args - the precompiled arguments generated by `format_args!`
 #[experimental = "libcore and I/O have yet to be reconciled, and this is an \
                   implementation detail which should not otherwise be exported"]
-pub fn write(output: &mut FormatWriter, args: Arguments) -> Result {
+pub fn write(output: &mut Writer, args: Arguments) -> Result {
     let mut formatter = Formatter {
         flags: 0,
         width: None,
@@ -296,16 +285,16 @@ pub fn write(output: &mut FormatWriter, args: Arguments) -> Result {
     match args.fmt {
         None => {
             // We can use default formatting parameters for all arguments.
-            for _ in range(0, args.args.len()) {
-                try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
-                try!(formatter.run(&DEFAULT_ARGUMENT));
+            for (arg, piece) in args.args.iter().zip(pieces.by_ref()) {
+                try!(formatter.buf.write_str(*piece));
+                try!((arg.formatter)(arg.value, &mut formatter));
             }
         }
         Some(fmt) => {
             // Every spec has a corresponding argument that is preceded by
             // a string piece.
             for (arg, piece) in fmt.iter().zip(pieces.by_ref()) {
-                try!(formatter.buf.write(piece.as_bytes()));
+                try!(formatter.buf.write_str(*piece));
                 try!(formatter.run(arg));
             }
         }
@@ -314,7 +303,7 @@ pub fn write(output: &mut FormatWriter, args: Arguments) -> Result {
     // There can be only one trailing string piece left.
     match pieces.next() {
         Some(piece) => {
-            try!(formatter.buf.write(piece.as_bytes()));
+            try!(formatter.buf.write_str(*piece));
         }
         None => {}
     }
@@ -378,7 +367,7 @@ impl<'a> Formatter<'a> {
     pub fn pad_integral(&mut self,
                         is_positive: bool,
                         prefix: &str,
-                        buf: &[u8])
+                        buf: &str)
                         -> Result {
         use char::Char;
         use fmt::rt::{FlagAlternate, FlagSignPlus, FlagSignAwareZeroPad};
@@ -402,9 +391,10 @@ impl<'a> Formatter<'a> {
             for c in sign.into_iter() {
                 let mut b = [0; 4];
                 let n = c.encode_utf8(&mut b).unwrap_or(0);
-                try!(f.buf.write(b[..n]));
+                let b = unsafe { str::from_utf8_unchecked(b[0..n]) };
+                try!(f.buf.write_str(b));
             }
-            if prefixed { f.buf.write(prefix.as_bytes()) }
+            if prefixed { f.buf.write_str(prefix) }
             else { Ok(()) }
         };
 
@@ -413,24 +403,26 @@ impl<'a> Formatter<'a> {
             // If there's no minimum length requirements then we can just
             // write the bytes.
             None => {
-                try!(write_prefix(self)); self.buf.write(buf)
+                try!(write_prefix(self)); self.buf.write_str(buf)
             }
             // Check if we're over the minimum width, if so then we can also
             // just write the bytes.
             Some(min) if width >= min => {
-                try!(write_prefix(self)); self.buf.write(buf)
+                try!(write_prefix(self)); self.buf.write_str(buf)
             }
             // The sign and prefix goes before the padding if the fill character
             // is zero
             Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => {
                 self.fill = '0';
                 try!(write_prefix(self));
-                self.with_padding(min - width, rt::AlignRight, |f| f.buf.write(buf))
+                self.with_padding(min - width, rt::AlignRight, |f| {
+                    f.buf.write_str(buf)
+                })
             }
             // Otherwise, the sign and prefix goes after the padding
             Some(min) => {
                 self.with_padding(min - width, rt::AlignRight, |f| {
-                    try!(write_prefix(f)); f.buf.write(buf)
+                    try!(write_prefix(f)); f.buf.write_str(buf)
                 })
             }
         }
@@ -451,7 +443,7 @@ impl<'a> Formatter<'a> {
     pub fn pad(&mut self, s: &str) -> Result {
         // Make sure there's a fast path up front
         if self.width.is_none() && self.precision.is_none() {
-            return self.buf.write(s.as_bytes());
+            return self.buf.write_str(s);
         }
         // The `precision` field can be interpreted as a `max-width` for the
         // string being formatted
@@ -463,7 +455,7 @@ impl<'a> Formatter<'a> {
                 let char_len = s.char_len();
                 if char_len >= max {
                     let nchars = ::cmp::min(max, char_len);
-                    return self.buf.write(s.slice_chars(0, nchars).as_bytes());
+                    return self.buf.write_str(s.slice_chars(0, nchars));
                 }
             }
             None => {}
@@ -472,17 +464,17 @@ impl<'a> Formatter<'a> {
         match self.width {
             // If we're under the maximum length, and there's no minimum length
             // requirements, then we can just emit the string
-            None => self.buf.write(s.as_bytes()),
+            None => self.buf.write_str(s),
             // If we're under the maximum width, check if we're over the minimum
             // width, if so it's as easy as just emitting the string.
             Some(width) if s.char_len() >= width => {
-                self.buf.write(s.as_bytes())
+                self.buf.write_str(s)
             }
             // If we're under both the maximum and the minimum width, then fill
             // up the minimum width with the specified string + some alignment.
             Some(width) => {
                 self.with_padding(width - s.char_len(), rt::AlignLeft, |me| {
-                    me.buf.write(s.as_bytes())
+                    me.buf.write_str(s)
                 })
             }
         }
@@ -507,15 +499,16 @@ impl<'a> Formatter<'a> {
 
         let mut fill = [0u8; 4];
         let len = self.fill.encode_utf8(&mut fill).unwrap_or(0);
+        let fill = unsafe { str::from_utf8_unchecked(fill[..len]) };
 
         for _ in range(0, pre_pad) {
-            try!(self.buf.write(fill[..len]));
+            try!(self.buf.write_str(fill));
         }
 
         try!(f(self));
 
         for _ in range(0, post_pad) {
-            try!(self.buf.write(fill[..len]));
+            try!(self.buf.write_str(fill));
         }
 
         Ok(())
@@ -524,8 +517,8 @@ impl<'a> Formatter<'a> {
     /// Writes some data to the underlying buffer contained within this
     /// formatter.
     #[unstable = "reconciling core and I/O may alter this definition"]
-    pub fn write(&mut self, data: &[u8]) -> Result {
-        self.buf.write(data)
+    pub fn write_str(&mut self, data: &str) -> Result {
+        self.buf.write_str(data)
     }
 
     /// Writes some formatted information into this instance
@@ -616,7 +609,9 @@ impl Show for char {
 impl<T> Pointer for *const T {
     fn fmt(&self, f: &mut Formatter) -> Result {
         f.flags |= 1 << (rt::FlagAlternate as uint);
-        LowerHex::fmt(&(*self as uint), f)
+        let ret = LowerHex::fmt(&(*self as uint), f);
+        f.flags &= !(1 << (rt::FlagAlternate as uint));
+        ret
     }
 }
 
diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs
index 7de3e847dc6..4f0cecbb243 100644
--- a/src/libcore/fmt/num.rs
+++ b/src/libcore/fmt/num.rs
@@ -18,6 +18,7 @@ use fmt;
 use iter::DoubleEndedIteratorExt;
 use num::{Int, cast};
 use slice::SliceExt;
+use str;
 
 /// A type that represents a specific radix
 #[doc(hidden)]
@@ -60,7 +61,8 @@ trait GenericRadix {
                 if x == zero { break };                   // No more digits left to accumulate.
             }
         }
-        f.pad_integral(is_positive, self.prefix(), buf[curr..])
+        let buf = unsafe { str::from_utf8_unchecked(buf[curr..]) };
+        f.pad_integral(is_positive, self.prefix(), buf)
     }
 }