diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-12-12 10:59:41 -0800 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-01-01 22:04:46 -0800 |
| commit | e423fcf0e0166da55f88233e0be5eacba55bc0bc (patch) | |
| tree | 0c4a4ffadfa870dcf23e1d55b224a1d7c2805f2a /src/libcore | |
| parent | cd614164e692cca3a1460737f581fcb6d4630baf (diff) | |
| download | rust-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.rs | 20 | ||||
| -rw-r--r-- | src/libcore/fmt/mod.rs | 71 | ||||
| -rw-r--r-- | src/libcore/fmt/num.rs | 4 |
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) } } |
