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`.
This commit is contained in:
parent
696367fb8d
commit
5aaa606932
@ -116,11 +116,25 @@ impl<'a> Arguments<'a> {
|
||||
#[cfg(not(stage0))]
|
||||
#[doc(hidden)] #[inline]
|
||||
pub unsafe fn new<'a>(pieces: &'static [&'static str],
|
||||
fmt: &'static [rt::Argument<'static>],
|
||||
args: &'a [Argument<'a>]) -> Arguments<'a> {
|
||||
Arguments {
|
||||
pieces: mem::transmute(pieces),
|
||||
fmt: mem::transmute(fmt),
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -144,8 +158,14 @@ impl<'a> Arguments<'a> {
|
||||
/// and `format` functions can be safely performed.
|
||||
#[cfg(not(stage0))]
|
||||
pub struct Arguments<'a> {
|
||||
// Format string pieces to print.
|
||||
pieces: &'a [&'a str],
|
||||
fmt: &'a [rt::Argument<'a>],
|
||||
|
||||
// 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>],
|
||||
}
|
||||
|
||||
@ -276,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.
|
||||
@ -299,11 +331,25 @@ pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
|
||||
|
||||
let mut pieces = args.pieces.iter();
|
||||
|
||||
for arg in args.fmt.iter() {
|
||||
try!(formatter.buf.write(pieces.next().unwrap().as_bytes()));
|
||||
try!(formatter.run(arg));
|
||||
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()));
|
||||
|
@ -56,6 +56,9 @@ struct Context<'a, 'b:'a> {
|
||||
pieces: Vec<Gc<ast::Expr>>,
|
||||
/// Collection of string literals
|
||||
str_pieces: Vec<Gc<ast::Expr>>,
|
||||
/// Stays `true` if all formatting parameters are default (as in "{}{}").
|
||||
all_pieces_simple: bool,
|
||||
|
||||
name_positions: HashMap<String, uint>,
|
||||
method_statics: Vec<Gc<ast::Item>>,
|
||||
|
||||
@ -383,7 +386,6 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
/// Translate a `parse::Piece` to a static `rt::Argument` or append
|
||||
/// to the `literal` string.
|
||||
fn trans_piece(&mut self, piece: &parse::Piece) -> Option<Gc<ast::Expr>> {
|
||||
// let mut is_not_default = true;
|
||||
let sp = self.fmtsp;
|
||||
match *piece {
|
||||
parse::String(s) => {
|
||||
@ -416,8 +418,25 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
}
|
||||
};
|
||||
|
||||
// Translate the format
|
||||
let simple_arg = parse::Argument {
|
||||
position: parse::ArgumentNext,
|
||||
format: parse::FormatSpec {
|
||||
fill: arg.format.fill,
|
||||
align: parse::AlignUnknown,
|
||||
flags: 0,
|
||||
precision: parse::CountImplied,
|
||||
width: parse::CountImplied,
|
||||
ty: arg.format.ty
|
||||
}
|
||||
};
|
||||
|
||||
let fill = match arg.format.fill { Some(c) => c, None => ' ' };
|
||||
|
||||
if *arg != simple_arg || fill != ' ' {
|
||||
self.all_pieces_simple = false;
|
||||
}
|
||||
|
||||
// Translate the format
|
||||
let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
|
||||
let align = match arg.format.align {
|
||||
parse::AlignLeft => {
|
||||
@ -453,6 +472,26 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
fn item_static_array(&self,
|
||||
name: ast::Ident,
|
||||
piece_ty: Gc<ast::Ty>,
|
||||
pieces: Vec<Gc<ast::Expr>>)
|
||||
-> ast::Stmt
|
||||
{
|
||||
let pieces_len = self.ecx.expr_uint(self.fmtsp, pieces.len());
|
||||
let fmt = self.ecx.expr_vec(self.fmtsp, pieces);
|
||||
let ty = ast::TyFixedLengthVec(
|
||||
piece_ty,
|
||||
pieces_len
|
||||
);
|
||||
let ty = self.ecx.ty(self.fmtsp, ty);
|
||||
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
|
||||
let item = self.ecx.item(self.fmtsp, name,
|
||||
self.static_attrs(), st);
|
||||
let decl = respan(self.fmtsp, ast::DeclItem(item));
|
||||
respan(self.fmtsp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))
|
||||
}
|
||||
|
||||
/// Actually builds the expression which the iformat! block will be expanded
|
||||
/// to
|
||||
fn to_expr(&self, invocation: Invocation) -> Gc<ast::Expr> {
|
||||
@ -471,54 +510,31 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
|
||||
// Next, build up the static array which will become our precompiled
|
||||
// format "string"
|
||||
let fmt = self.ecx.expr_vec(self.fmtsp, self.str_pieces.clone());
|
||||
let piece_ty = self.ecx.ty_rptr(self.fmtsp,
|
||||
self.ecx.ty_ident(self.fmtsp,
|
||||
self.ecx.ident_of("str")),
|
||||
Some(self.ecx.lifetime(self.fmtsp,
|
||||
self.ecx.ident_of(
|
||||
"'static").name)),
|
||||
ast::MutImmutable);
|
||||
|
||||
let ty = ast::TyFixedLengthVec(
|
||||
piece_ty,
|
||||
self.ecx.expr_uint(self.fmtsp, self.str_pieces.len())
|
||||
);
|
||||
let ty = self.ecx.ty(self.fmtsp, ty);
|
||||
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
|
||||
let static_str_name = self.ecx.ident_of("__STATIC_FMTSTR");
|
||||
let item = self.ecx.item(self.fmtsp, static_str_name,
|
||||
self.static_attrs(), st);
|
||||
let decl = respan(self.fmtsp, ast::DeclItem(item));
|
||||
lets.push(box(GC) respan(self.fmtsp,
|
||||
ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
|
||||
|
||||
// Then, build up the static array which will become our precompiled
|
||||
// format "string"
|
||||
let fmt = self.ecx.expr_vec(self.fmtsp, self.pieces.clone());
|
||||
let piece_ty = self.ecx.ty_path(self.ecx.path_all(
|
||||
let static_lifetime = self.ecx.lifetime(self.fmtsp, self.ecx.ident_of("'static").name);
|
||||
let piece_ty = self.ecx.ty_rptr(
|
||||
self.fmtsp,
|
||||
true, vec!(
|
||||
self.ecx.ident_of("std"),
|
||||
self.ecx.ident_of("fmt"),
|
||||
self.ecx.ident_of("rt"),
|
||||
self.ecx.ident_of("Argument")),
|
||||
vec!(self.ecx.lifetime(self.fmtsp,
|
||||
self.ecx.ident_of("'static").name)),
|
||||
Vec::new()
|
||||
), None);
|
||||
let ty = ast::TyFixedLengthVec(
|
||||
piece_ty,
|
||||
self.ecx.expr_uint(self.fmtsp, self.pieces.len())
|
||||
);
|
||||
let ty = self.ecx.ty(self.fmtsp, ty);
|
||||
let st = ast::ItemStatic(ty, ast::MutImmutable, fmt);
|
||||
self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
|
||||
Some(static_lifetime),
|
||||
ast::MutImmutable);
|
||||
lets.push(box(GC) self.item_static_array(static_str_name,
|
||||
piece_ty,
|
||||
self.str_pieces.clone()));
|
||||
|
||||
// Then, build up the static array which will store our precompiled
|
||||
// nonstandard placeholders, if there are any.
|
||||
let static_args_name = self.ecx.ident_of("__STATIC_FMTARGS");
|
||||
let item = self.ecx.item(self.fmtsp, static_args_name,
|
||||
self.static_attrs(), st);
|
||||
let decl = respan(self.fmtsp, ast::DeclItem(item));
|
||||
lets.push(box(GC) respan(self.fmtsp,
|
||||
ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)));
|
||||
if !self.all_pieces_simple {
|
||||
let piece_ty = self.ecx.ty_path(self.ecx.path_all(
|
||||
self.fmtsp,
|
||||
true, self.rtpath("Argument"),
|
||||
vec![static_lifetime],
|
||||
vec![]
|
||||
), None);
|
||||
lets.push(box(GC) self.item_static_array(static_args_name,
|
||||
piece_ty,
|
||||
self.pieces.clone()));
|
||||
}
|
||||
|
||||
// Right now there is a bug such that for the expression:
|
||||
// foo(bar(&1))
|
||||
@ -565,13 +581,20 @@ impl<'a, 'b> Context<'a, 'b> {
|
||||
|
||||
// Now create the fmt::Arguments struct with all our locals we created.
|
||||
let pieces = self.ecx.expr_ident(self.fmtsp, static_str_name);
|
||||
let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
|
||||
let args_slice = self.ecx.expr_ident(self.fmtsp, slicename);
|
||||
|
||||
let (fn_name, fn_args) = if self.all_pieces_simple {
|
||||
("new", vec![pieces, args_slice])
|
||||
} else {
|
||||
let fmt = self.ecx.expr_ident(self.fmtsp, static_args_name);
|
||||
("with_placeholders", vec![pieces, fmt, args_slice])
|
||||
};
|
||||
|
||||
let result = self.ecx.expr_call_global(self.fmtsp, vec!(
|
||||
self.ecx.ident_of("std"),
|
||||
self.ecx.ident_of("fmt"),
|
||||
self.ecx.ident_of("Arguments"),
|
||||
self.ecx.ident_of("new")), vec!(pieces, fmt, args_slice));
|
||||
self.ecx.ident_of(fn_name)), fn_args);
|
||||
|
||||
// We did all the work of making sure that the arguments
|
||||
// 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,
|
||||
literal: String::new(),
|
||||
pieces: Vec::new(),
|
||||
str_pieces: Vec::new(),
|
||||
all_pieces_simple: true,
|
||||
method_statics: Vec::new(),
|
||||
fmtsp: sp,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user