Skip to content

Commit 5aaa606

Browse files
committed
Optimize for the most common cases of format!
Format specs are ignored and not stored in case they're all default. Restore default formatting parameters during iteration. Pass `None` instead of empty slices of format specs to take advantage of non-nullable pointer optimization. Generate a call to one of two functions of `fmt::Argument`.
1 parent 696367f commit 5aaa606

File tree

2 files changed

+125
-55
lines changed

2 files changed

+125
-55
lines changed

src/libcore/fmt/mod.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,25 @@ impl<'a> Arguments<'a> {
116116
#[cfg(not(stage0))]
117117
#[doc(hidden)] #[inline]
118118
pub unsafe fn new<'a>(pieces: &'static [&'static str],
119-
fmt: &'static [rt::Argument<'static>],
120119
args: &'a [Argument<'a>]) -> Arguments<'a> {
121120
Arguments {
122121
pieces: mem::transmute(pieces),
123-
fmt: mem::transmute(fmt),
122+
fmt: None,
123+
args: args
124+
}
125+
}
126+
127+
/// This function is used to specify nonstandard formatting parameters.
128+
/// The `pieces` array must be at least as long as `fmt` to construct
129+
/// a valid Arguments structure.
130+
#[cfg(not(stage0))]
131+
#[doc(hidden)] #[inline]
132+
pub unsafe fn with_placeholders<'a>(pieces: &'static [&'static str],
133+
fmt: &'static [rt::Argument<'static>],
134+
args: &'a [Argument<'a>]) -> Arguments<'a> {
135+
Arguments {
136+
pieces: mem::transmute(pieces),
137+
fmt: Some(mem::transmute(fmt)),
124138
args: args
125139
}
126140
}
@@ -144,8 +158,14 @@ impl<'a> Arguments<'a> {
144158
/// and `format` functions can be safely performed.
145159
#[cfg(not(stage0))]
146160
pub struct Arguments<'a> {
161+
// Format string pieces to print.
147162
pieces: &'a [&'a str],
148-
fmt: &'a [rt::Argument<'a>],
163+
164+
// Placeholder specs, or `None` if all specs are default (as in "{}{}").
165+
fmt: Option<&'a [rt::Argument<'a>]>,
166+
167+
// Dynamic arguments for interpolation, to be interleaved with string
168+
// pieces. (Every argument is preceded by a string piece.)
149169
args: &'a [Argument<'a>],
150170
}
151171

@@ -276,6 +296,18 @@ uniform_fn_call_workaround! {
276296
secret_upper_exp, UpperExp;
277297
}
278298

299+
#[cfg(not(stage0))]
300+
static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
301+
position: rt::ArgumentNext,
302+
format: rt::FormatSpec {
303+
fill: ' ',
304+
align: rt::AlignUnknown,
305+
flags: 0,
306+
precision: rt::CountImplied,
307+
width: rt::CountImplied,
308+
}
309+
};
310+
279311
/// The `write` function takes an output stream, a precompiled format string,
280312
/// and a list of arguments. The arguments will be formatted according to the
281313
/// specified format string into the output stream provided.
@@ -299,11 +331,25 @@ pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
299331

300332
let mut pieces = args.pieces.iter();
301333

302-
for arg in args.fmt.iter() {
303-
try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
304-
try!(formatter.run(arg));
334+
match args.fmt {
335+
None => {
336+
// We can use default formatting parameters for all arguments.
337+
for _ in range(0, args.args.len()) {
338+
try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
339+
try!(formatter.run(&DEFAULT_ARGUMENT));
340+
}
341+
}
342+
Some(fmt) => {
343+
// Every spec has a corresponding argument that is preceded by
344+
// a string piece.
345+
for (arg, piece) in fmt.iter().zip(pieces.by_ref()) {
346+
try!(formatter.buf.write(piece.as_bytes()));
347+
try!(formatter.run(arg));
348+
}
349+
}
305350
}
306351

352+
// There can be only one trailing string piece left.
307353
match pieces.next() {
308354
Some(piece) => {
309355
try!(formatter.buf.write(piece.as_bytes()));

src/libsyntax/ext/format.rs

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ struct Context<'a, 'b:'a> {
5656
pieces: Vec<Gc<ast::Expr>>,
5757
/// Collection of string literals
5858
str_pieces: Vec<Gc<ast::Expr>>,
59+
/// Stays `true` if all formatting parameters are default (as in "{}{}").
60+
all_pieces_simple: bool,
61+
5962
name_positions: HashMap<String, uint>,
6063
method_statics: Vec<Gc<ast::Item>>,
6164

@@ -383,7 +386,6 @@ impl<'a, 'b> Context<'a, 'b> {
383386
/// Translate a `parse::Piece` to a static `rt::Argument` or append
384387
/// to the `literal` string.
385388
fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
386-
// let mut is_not_default = true;
387389
let sp = self.fmtsp;
388390
match *piece {
389391
parse::String(s) => {
@@ -416,8 +418,25 @@ impl<'a, 'b> Context<'a, 'b> {
416418
}
417419
};
418420

419-
// Translate the format
421+
let simple_arg = parse::Argument {
422+
position: parse::ArgumentNext,
423+
format: parse::FormatSpec {
424+
fill: arg.format.fill,
425+
align: parse::AlignUnknown,
426+
flags: 0,
427+
precision: parse::CountImplied,
428+
width: parse::CountImplied,
429+
ty: arg.format.ty
430+
}
431+
};
432+
420433
let fill = match arg.format.fill { Some(c) => c, None => ' ' };
434+
435+
if *arg != simple_arg || fill != ' ' {
436+
self.all_pieces_simple = false;
437+
}
438+
439+
// Translate the format
421440
let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
422441
let align = match arg.format.align {
423442
parse::AlignLeft => {
@@ -453,6 +472,26 @@ impl<'a, 'b> Context<'a, 'b> {
453472
}
454473
}
455474

475+
fn item_static_array(&self,
476+
name: ast::Ident,
477+
piece_ty: Gc<ast::Ty>,
478+
pieces: Vec<Gc<ast::Expr>>)
479+
-> ast::Stmt
480+
{
481+
let pieces_len = self.ecx.expr_uint(self.fmtsp, pieces.len());
482+
let fmt = self.ecx.expr_vec(self.fmtsp, pieces);
483+
let ty = ast::TyFixedLengthVec(
484+
piece_ty,
485+
pieces_len
486+
);
487+
let ty = self.ecx.ty(self.fmtsp, ty);
488+
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
489+
let item = self.ecx.item(self.fmtsp, name,
490+
self.static_attrs(), st);
491+
let decl = respan(self.fmtsp, ast::DeclItem(item));
492+
respan(self.fmtsp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))
493+
}
494+
456495
/// Actually builds the expression which the iformat! block will be expanded
457496
/// to
458497
fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
@@ -471,54 +510,31 @@ impl<'a, 'b> Context<'a, 'b> {
471510

472511
// Next, build up the static array which will become our precompiled
473512
// format "string"
474-
let fmt = self.ecx.expr_vec(self.fmtsp, self.str_pieces.clone());
475-
let piece_ty = self.ecx.ty_rptr(self.fmtsp,
476-
self.ecx.ty_ident(self.fmtsp,
477-
self.ecx.ident_of("str")),
478-
Some(self.ecx.lifetime(self.fmtsp,
479-
self.ecx.ident_of(
480-
"'static").name)),
481-
ast::MutImmutable);
482-
483-
let ty = ast::TyFixedLengthVec(
484-
piece_ty,
485-
self.ecx.expr_uint(self.fmtsp, self.str_pieces.len())
486-
);
487-
let ty = self.ecx.ty(self.fmtsp, ty);
488-
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
489513
let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
490-
let item = self.ecx.item(self.fmtsp, static_str_name,
491-
self.static_attrs(), st);
492-
let decl = respan(self.fmtsp, ast::DeclItem(item));
493-
lets.push(box(GC) respan(self.fmtsp,
494-
ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
495-
496-
// Then, build up the static array which will become our precompiled
497-
// format "string"
498-
let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
499-
let piece_ty = self.ecx.ty_path(self.ecx.path_all(
514+
let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
515+
let piece_ty = self.ecx.ty_rptr(
500516
self.fmtsp,
501-
true, vec!(
502-
self.ecx.ident_of("std"),
503-
self.ecx.ident_of("fmt"),
504-
self.ecx.ident_of("rt"),
505-
self.ecx.ident_of("Argument")),
506-
vec!(self.ecx.lifetime(self.fmtsp,
507-
self.ecx.ident_of("'static").name)),
508-
Vec::new()
509-
), None);
510-
let ty = ast::TyFixedLengthVec(
511-
piece_ty,
512-
self.ecx.expr_uint(self.fmtsp, self.pieces.len())
513-
);
514-
let ty = self.ecx.ty(self.fmtsp, ty);
515-
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
517+
self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
518+
Some(static_lifetime),
519+
ast::MutImmutable);
520+
lets.push(box(GC) self.item_static_array(static_str_name,
521+
piece_ty,
522+
self.str_pieces.clone()));
523+
524+
// Then, build up the static array which will store our precompiled
525+
// nonstandard placeholders, if there are any.
516526
let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
517-
let item = self.ecx.item(self.fmtsp, static_args_name,
518-
self.static_attrs(), st);
519-
let decl = respan(self.fmtsp, ast::DeclItem(item));
520-
lets.push(box(GC) respan(self.fmtsp,
521-
ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
527+
if !self.all_pieces_simple {
528+
let piece_ty = self.ecx.ty_path(self.ecx.path_all(
529+
self.fmtsp,
530+
true, self.rtpath("Argument"),
531+
vec![static_lifetime],
532+
vec![]
533+
), None);
534+
lets.push(box(GC) self.item_static_array(static_args_name,
535+
piece_ty,
536+
self.pieces.clone()));
537+
}
522538

523539
// Right now there is a bug such that for the expression:
524540
// foo(bar(&1))
@@ -565,13 +581,20 @@ impl<'a, 'b> Context<'a, 'b> {
565581

566582
// Now create the fmt::Arguments struct with all our locals we created.
567583
let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
568-
let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
569584
let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
585+
586+
let (fn_name, fn_args) = if self.all_pieces_simple {
587+
("new", vec![pieces, args_slice])
588+
} else {
589+
let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
590+
("with_placeholders", vec![pieces, fmt, args_slice])
591+
};
592+
570593
let result = self.ecx.expr_call_global(self.fmtsp, vec!(
571594
self.ecx.ident_of("std"),
572595
self.ecx.ident_of("fmt"),
573596
self.ecx.ident_of("Arguments"),
574-
self.ecx.ident_of("new")), vec!(pieces, fmt, args_slice));
597+
self.ecx.ident_of(fn_name)), fn_args);
575598

576599
// We did all the work of making sure that the arguments
577600
// structure is safe, so we can safely have an unsafe block.
@@ -741,6 +764,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
741764
literal: String::new(),
742765
pieces: Vec::new(),
743766
str_pieces: Vec::new(),
767+
all_pieces_simple: true,
744768
method_statics: Vec::new(),
745769
fmtsp: sp,
746770
};

0 commit comments

Comments
 (0)