about summary refs log tree commit diff
path: root/src/libsyntax/ext/fmt.rs
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2013-03-20 23:33:10 -0400
committerAlex Crichton <alex@alexcrichton.com>2013-03-22 11:49:45 -0400
commite93654c96d0288e6f2f00075d95dd4958b4cb4dc (patch)
tree5fc8b18de7acef9b31c276beec3315d24d374bef /src/libsyntax/ext/fmt.rs
parentc0bbc6242fa80d7431cc7ea99a3b20f4eb8595ab (diff)
downloadrust-e93654c96d0288e6f2f00075d95dd4958b4cb4dc.tar.gz
rust-e93654c96d0288e6f2f00075d95dd4958b4cb4dc.zip
Pass the fmt! buffer to each conversion method
Achieves a little more speedup and avoids allocations around some strings in
conv_str
Diffstat (limited to 'src/libsyntax/ext/fmt.rs')
-rw-r--r--src/libsyntax/ext/fmt.rs117
1 files changed, 59 insertions, 58 deletions
diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs
index 4c7dd454983..3ebe844950a 100644
--- a/src/libsyntax/ext/fmt.rs
+++ b/src/libsyntax/ext/fmt.rs
@@ -139,19 +139,17 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
         make_conv_struct(cx, sp, rt_conv_flags, rt_conv_width,
                          rt_conv_precision, rt_conv_ty)
     }
-    fn make_conv_call(cx: @ext_ctxt, sp: span, conv_type: ~str, cnv: &Conv,
-                      arg: @ast::expr) -> @ast::expr {
+    fn make_conv_call(cx: @ext_ctxt, sp: span, conv_type: &str, cnv: &Conv,
+                      arg: @ast::expr, buf: @ast::expr) -> @ast::expr {
         let fname = ~"conv_" + conv_type;
         let path = make_path_vec(cx, @fname);
         let cnv_expr = make_rt_conv_expr(cx, sp, cnv);
-        let args = ~[cnv_expr, arg];
+        let args = ~[cnv_expr, arg, buf];
         return mk_call_global(cx, arg.span, path, args);
     }
 
-    fn make_new_conv(cx: @ext_ctxt, sp: span, cnv: &Conv, arg: @ast::expr) ->
-       @ast::expr {
-        // FIXME: Move validation code into core::extfmt (Issue #2249)
-
+    fn make_new_conv(cx: @ext_ctxt, sp: span, cnv: &Conv,
+                     arg: @ast::expr, buf: @ast::expr) -> @ast::expr {
         fn is_signed_type(cnv: &Conv) -> bool {
             match cnv.ty {
               TyInt(s) => match s {
@@ -198,27 +196,17 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
           CountIs(_) => (),
           _ => cx.span_unimpl(sp, unsupported)
         }
-        match cnv.ty {
-          TyStr => return make_conv_call(cx, arg.span, ~"str", cnv, arg),
-          TyInt(sign) => match sign {
-            Signed => return make_conv_call(cx, arg.span, ~"int", cnv, arg),
-            Unsigned => {
-                return make_conv_call(cx, arg.span, ~"uint", cnv, arg)
-            }
-          },
-          TyBool => return make_conv_call(cx, arg.span, ~"bool", cnv, arg),
-          TyChar => return make_conv_call(cx, arg.span, ~"char", cnv, arg),
-          TyHex(_) => {
-            return make_conv_call(cx, arg.span, ~"uint", cnv, arg);
-          }
-          TyBits => return make_conv_call(cx, arg.span, ~"uint", cnv, arg),
-          TyOctal => return make_conv_call(cx, arg.span, ~"uint", cnv, arg),
-          TyFloat => {
-            return make_conv_call(cx, arg.span, ~"float", cnv, arg);
-          }
-          TyPoly => return make_conv_call(cx, arg.span, ~"poly", cnv,
-                       mk_addr_of(cx, sp, arg))
-        }
+        let (name, actual_arg) = match cnv.ty {
+            TyStr => ("str", arg),
+            TyInt(Signed) => ("int", arg),
+            TyBool => ("bool", arg),
+            TyChar => ("char", arg),
+            TyBits | TyOctal | TyHex(_) | TyInt(Unsigned) => ("uint", arg),
+            TyFloat => ("float", arg),
+            TyPoly => ("poly", mk_addr_of(cx, sp, arg))
+        };
+        return make_conv_call(cx, arg.span, name, cnv, actual_arg,
+                              mk_mut_addr_of(cx, arg.span, buf));
     }
     fn log_conv(c: &Conv) {
         debug!("Building conversion:");
@@ -270,14 +258,41 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
         }
     }
 
-    /* Translate each piece (portion of the fmt expression) into a ~str
-       expression to be concatenated below */
     let fmt_sp = args[0].span;
     let mut n = 0u;
     let nargs = args.len();
-    let pieces = do vec::map_consume(pieces) |pc| {
+
+    /* 'ident' is the local buffer building up the result of fmt! */
+    let ident = cx.parse_sess().interner.intern(@~"__fmtbuf");
+    let buf = || mk_path(cx, fmt_sp, ~[ident]);
+    let str_ident = cx.parse_sess().interner.intern(@~"str");
+    let push_ident = cx.parse_sess().interner.intern(@~"push_str");
+    let mut stms = ~[];
+
+    /* Translate each piece (portion of the fmt expression) by invoking the
+       corresponding function in core::unstable::extfmt. Each function takes a
+       buffer to insert data into along with the data being formatted. */
+    do vec::consume(pieces) |i, pc| {
         match pc {
-            PieceString(s) => mk_uniq_str(cx, fmt_sp, s),
+            /* Raw strings get appended via str::push_str */
+            PieceString(s) => {
+                let portion = mk_uniq_str(cx, fmt_sp, s);
+
+                /* If this is the first portion, then initialize the local
+                   buffer with it directly */
+                if i == 0 {
+                    stms.push(mk_local(cx, fmt_sp, true, ident, portion));
+                } else {
+                    let args = ~[mk_mut_addr_of(cx, fmt_sp, buf()), portion];
+                    let call = mk_call_global(cx,
+                                              fmt_sp,
+                                              ~[str_ident, push_ident],
+                                              args);
+                    stms.push(mk_stmt(cx, fmt_sp, call));
+                }
+            }
+
+            /* Invoke the correct conv function in extfmt */
             PieceConv(ref conv) => {
                 n += 1u;
                 if n >= nargs {
@@ -285,11 +300,21 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
                                   ~"not enough arguments to fmt! " +
                                   ~"for the given format string");
                 }
+
                 log_conv(conv);
-                make_new_conv(cx, fmt_sp, conv, args[n])
+                /* If the first portion is a conversion, then the local buffer
+                   must be initialized as an empty string */
+                if i == 0 {
+                    stms.push(mk_local(cx, fmt_sp, true, ident,
+                                       mk_uniq_str(cx, fmt_sp, ~"")));
+                }
+                stms.push(mk_stmt(cx, fmt_sp,
+                                  make_new_conv(cx, fmt_sp, conv,
+                                                args[n], buf())));
             }
         }
-    };
+    }
+
     let expected_nargs = n + 1u; // n conversions + the fmt string
     if expected_nargs < nargs {
         cx.span_fatal
@@ -297,30 +322,6 @@ fn pieces_to_expr(cx: @ext_ctxt, sp: span,
                            nargs, expected_nargs));
     }
 
-    /* Concatenate all of the strings together with str::push_str. This
-       involves storing the first piece into a local variable, and then
-       pushing each other piece onto the local. The local is contained in its
-       own block to not conflict with other names as much as possible */
-    let ident = cx.parse_sess().interner.intern(@~"__fmtbuf");
-    let buf = || mk_path(cx, fmt_sp, ~[ident]);
-    let str_ident = cx.parse_sess().interner.intern(@~"str");
-    let push_ident = cx.parse_sess().interner.intern(@~"push_str");
-
-    let mut first = true;
-    let stms = do vec::map_consume(pieces) |pc| {
-        if first {
-            first = false;
-            mk_local(cx, fmt_sp, true, ident, pc)
-        } else {
-            let call = mk_call_global(cx,
-                                      fmt_sp,
-                                      ~[str_ident, push_ident],
-                                      ~[mk_mut_addr_of(cx, fmt_sp, buf()),
-                                        pc]);
-            mk_stmt(cx, fmt_sp, call)
-        }
-    };
-
     return mk_block(cx, fmt_sp, ~[], stms, Some(buf()));
 }
 //