diff options
| author | bors <bors@rust-lang.org> | 2014-09-09 23:55:43 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-09-09 23:55:43 +0000 |
| commit | a1f49730906ca27947cd99547f7295a1bcf83920 (patch) | |
| tree | 4894feeb3041bedbce1a7febdf2916e142dcbede /src/libcore | |
| parent | 651106462c357b71a4ca2c02ba2bfedfc38b0035 (diff) | |
| parent | fcf88b8f87597f97112f048eaa296b3127e51d0b (diff) | |
auto merge of #16662 : pczarn/rust/format-fmtstr-opt, r=brson
Based on an observation that strings and arguments are always interleaved, thanks to #15832. Additionally optimize invocations where formatting parameters are unspecified for all arguments, e.g. `"{} {:?} {:x}"`, by emptying the `__STATIC_FMTARGS` array. Next, `Arguments::new` replaces an empty slice with `None` so that passing empty `__STATIC_FMTARGS` generates slightly less machine code when `Arguments::new` is inlined. Furthermore, formatting itself treats these cases separately without making redundant copies of formatting parameters.
All in all, this adds a single mov instruction per `write!` in most cases. That's why code size has increased.
Diffstat (limited to 'src/libcore')
| -rw-r--r-- | src/libcore/fmt/mod.rs | 117 | ||||
| -rw-r--r-- | src/libcore/fmt/rt.rs | 2 |
2 files changed, 118 insertions, 1 deletions
diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 7c4494358b1..be75bfec32c 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -113,6 +113,33 @@ impl<'a> Arguments<'a> { /// Arguments structure. The compiler inserts an `unsafe` block to call this, /// which is valid because the compiler performs all necessary validation to /// ensure that the resulting call to format/write would be safe. + #[cfg(not(stage0))] + #[doc(hidden)] #[inline] + pub unsafe fn new<'a>(pieces: &'static [&'static str], + args: &'a [Argument<'a>]) -> Arguments<'a> { + Arguments { + pieces: mem::transmute(pieces), + fmt: None, + args: args + } + } + + /// This function is used to specify nonstandard formatting parameters. + /// The `pieces` array must be at least as long as `fmt` to construct + /// a valid Arguments structure. + #[cfg(not(stage0))] + #[doc(hidden)] #[inline] + pub unsafe fn with_placeholders<'a>(pieces: &'static [&'static str], + fmt: &'static [rt::Argument<'static>], + args: &'a [Argument<'a>]) -> Arguments<'a> { + Arguments { + pieces: mem::transmute(pieces), + fmt: Some(mem::transmute(fmt)), + args: args + } + } + + #[cfg(stage0)] #[doc(hidden)] #[inline] pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>], args: &'a [Argument<'a>]) -> Arguments<'a> { @@ -129,6 +156,20 @@ impl<'a> Arguments<'a> { /// and pass it to a function or closure, passed as the first argument. The /// macro validates the format string at compile-time so usage of the `write` /// and `format` functions can be safely performed. +#[cfg(not(stage0))] +pub struct Arguments<'a> { + // Format string pieces to print. + pieces: &'a [&'a str], + + // Placeholder specs, or `None` if all specs are default (as in "{}{}"). + fmt: Option<&'a [rt::Argument<'a>]>, + + // Dynamic arguments for interpolation, to be interleaved with string + // pieces. (Every argument is preceded by a string piece.) + args: &'a [Argument<'a>], +} + +#[cfg(stage0)] #[doc(hidden)] pub struct Arguments<'a> { fmt: &'a [rt::Piece<'a>], args: &'a [Argument<'a>], @@ -255,6 +296,18 @@ uniform_fn_call_workaround! { secret_upper_exp, UpperExp; } +#[cfg(not(stage0))] +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. @@ -263,6 +316,51 @@ uniform_fn_call_workaround! { /// /// * output - the buffer to write output to /// * args - the precompiled arguments generated by `format_args!` +#[cfg(not(stage0))] +pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result { + let mut formatter = Formatter { + flags: 0, + width: None, + precision: None, + buf: output, + align: rt::AlignUnknown, + fill: ' ', + args: args.args, + curarg: args.args.iter(), + }; + + let mut pieces = args.pieces.iter(); + + 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)); + } + } + 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.run(arg)); + } + } + } + + // There can be only one trailing string piece left. + match pieces.next() { + Some(piece) => { + try!(formatter.buf.write(piece.as_bytes())); + } + None => {} + } + + Ok(()) +} + +#[cfg(stage0)] #[doc(hidden)] pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result { let mut formatter = Formatter { flags: 0, @@ -285,7 +383,26 @@ impl<'a> Formatter<'a> { // First up is the collection of functions used to execute a format string // at runtime. This consumes all of the compile-time statics generated by // the format! syntax extension. + #[cfg(not(stage0))] + fn run(&mut self, arg: &rt::Argument) -> Result { + // Fill in the format parameters into the formatter + self.fill = arg.format.fill; + self.align = arg.format.align; + self.flags = arg.format.flags; + self.width = self.getcount(&arg.format.width); + self.precision = self.getcount(&arg.format.precision); + + // Extract the correct argument + let value = match arg.position { + rt::ArgumentNext => { *self.curarg.next().unwrap() } + rt::ArgumentIs(i) => self.args[i], + }; + + // Then actually do some printing + (value.formatter)(value.value, self) + } + #[cfg(stage0)] #[doc(hidden)] fn run(&mut self, piece: &rt::Piece) -> Result { match *piece { rt::String(s) => self.buf.write(s.as_bytes()), diff --git a/src/libcore/fmt/rt.rs b/src/libcore/fmt/rt.rs index 1f5449130ec..59fbde88d8b 100644 --- a/src/libcore/fmt/rt.rs +++ b/src/libcore/fmt/rt.rs @@ -14,7 +14,7 @@ //! These definitions are similar to their `ct` equivalents, but differ in that //! these can be statically allocated and are slightly optimized for the runtime - +#[cfg(stage0)] #[doc(hidden)] pub enum Piece<'a> { String(&'a str), |
