]> Cypherpunks repositories - gostls13.git/commitdiff
fmt: use fewer allocations for %q string formatting
authorMartin Möhrmann <martisch@uos.de>
Sat, 5 Mar 2016 10:33:13 +0000 (11:33 +0100)
committerRob Pike <r@golang.org>
Sat, 5 Mar 2016 22:04:24 +0000 (22:04 +0000)
Reuse the internal buffer and use append versions of
the strconv quote functions to avoid some allocations.

Add more tests.

name                  old time/op    new time/op    delta
SprintfQuoteString-2     486ns ± 2%     416ns ± 2%  -14.42%  (p=0.000 n=20+20)

name                  old allocs/op  new allocs/op  delta
SprintfQuoteString-2      4.00 ± 0%      2.00 ± 0%  -50.00%  (p=0.000 n=20+20)

Change-Id: I63795b51fd95c53c5993ec8e6e99b659941f9f54
Reviewed-on: https://go-review.googlesource.com/20251
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
src/fmt/fmt_test.go
src/fmt/format.go

index 797eeab1ea5101b893b40e3354847ea0f4f99db8..8287d305afa9ca1a750fb0c15c0a99b617962301 100644 (file)
@@ -174,29 +174,88 @@ var fmtTests = []struct {
        {"%# X", []byte("xyz"), "0X78 0X79 0X7A"},
 
        // escaped strings
-       {"%#q", `abc`, "`abc`"},
-       {"%#q", `"`, "`\"`"},
-       {"1 %#q", `\n`, "1 `\\n`"},
-       {"2 %#q", "\n", `2 "\n"`},
-       {"%q", `"`, `"\""`},
-       {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`},
+       {"%q", "", `""`},
+       {"%#q", "", "``"},
+       {"%q", "\"", `"\""`},
+       {"%#q", "\"", "`\"`"},
+       {"%q", "`", `"` + "`" + `"`},
+       {"%#q", "`", `"` + "`" + `"`},
+       {"%q", "\n", `"\n"`},
+       {"%#q", "\n", `"\n"`},
+       {"%q", `\n`, `"\\n"`},
+       {"%#q", `\n`, "`\\n`"},
+       {"%q", "abc", `"abc"`},
+       {"%#q", "abc", "`abc`"},
+       {"%q", "日本語", `"日本語"`},
+       {"%+q", "日本語", `"\u65e5\u672c\u8a9e"`},
+       {"%#q", "日本語", "`日本語`"},
+       {"%#+q", "日本語", "`日本語`"},
+       {"%q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`},
+       {"%+q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`},
+       {"%#q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`},
+       {"%#+q", "\a\b\f\n\r\t\v\"\\", `"\a\b\f\n\r\t\v\"\\"`},
+       {"%q", "☺", `"☺"`},
+       {"% q", "☺", `"☺"`}, // The space modifier should have no effect.
+       {"%+q", "☺", `"\u263a"`},
+       {"%#q", "☺", "`☺`"},
+       {"%#+q", "☺", "`☺`"},
+       {"%10q", "⌘", `       "⌘"`},
+       {"%+10q", "⌘", `  "\u2318"`},
+       {"%-10q", "⌘", `"⌘"       `},
+       {"%+-10q", "⌘", `"\u2318"  `},
+       {"%010q", "⌘", `0000000"⌘"`},
+       {"%+010q", "⌘", `00"\u2318"`},
+       {"%-010q", "⌘", `"⌘"       `}, // 0 has no effect when - is present.
+       {"%+-010q", "⌘", `"\u2318"  `},
+       {"%#8q", "\n", `    "\n"`},
+       {"%#+8q", "\r", `    "\r"`},
+       {"%#-8q", "\t", "`      `     "},
+       {"%#+-8q", "\b", `"\b"    `},
        {"%q", "abc\xffdef", `"abc\xffdef"`},
-       {"%q", "\u263a", `"☺"`},
-       {"%+q", "\u263a", `"\u263a"`},
-       {"%q", "\U0010ffff", `"\U0010ffff"`},
+       {"%+q", "abc\xffdef", `"abc\xffdef"`},
+       {"%#q", "abc\xffdef", `"abc\xffdef"`},
+       {"%#+q", "abc\xffdef", `"abc\xffdef"`},
+       {"%q", "\U0010ffff", `"\U0010ffff"`}, // Rune is not printable.
+       {"%+q", "\U0010ffff", `"\U0010ffff"`},
+       {"%#q", "\U0010ffff", "`􏿿`"},
+       {"%#+q", "\U0010ffff", "`􏿿`"},
+       {"%q", string(0x110000), `"�"`}, // Rune is not valid.
+       {"%+q", string(0x110000), `"\ufffd"`},
+       {"%#q", string(0x110000), "`�`"},
+       {"%#+q", string(0x110000), "`�`"},
 
        // escaped characters
-       {"%q", 'x', `'x'`},
        {"%q", 0, `'\x00'`},
+       {"%+q", 0, `'\x00'`},
+       {"%q", '"', `'"'`},
+       {"%+q", '"', `'"'`},
+       {"%q", '\'', `'\''`},
+       {"%+q", '\'', `'\''`},
+       {"%q", '`', "'`'"},
+       {"%+q", '`', "'`'"},
+       {"%q", 'x', `'x'`},
+       {"%+q", 'x', `'x'`},
+       {"%q", 'ÿ', `'ÿ'`},
+       {"%+q", 'ÿ', `'\u00ff'`},
        {"%q", '\n', `'\n'`},
-       {"%q", '\u0e00', `'\u0e00'`},         // not a printable rune.
-       {"%q", '\U000c2345', `'\U000c2345'`}, // not a printable rune.
+       {"%+q", '\n', `'\n'`},
+       {"%q", '☺', `'☺'`},
+       {"% q", '☺', `'☺'`}, // The space modifier should have no effect.
+       {"%+q", '☺', `'\u263a'`},
+       {"%10q", '⌘', `       '⌘'`},
+       {"%+10q", '⌘', `  '\u2318'`},
+       {"%-10q", '⌘', `'⌘'       `},
+       {"%+-10q", '⌘', `'\u2318'  `},
+       {"%010q", '⌘', `0000000'⌘'`},
+       {"%+010q", '⌘', `00'\u2318'`},
+       {"%-010q", '⌘', `'⌘'       `}, // 0 has no effect when - is present.
+       {"%+-010q", '⌘', `'\u2318'  `},
+       {"%q", '\U00000e00', `'\u0e00'`},             // Rune is not printable.
+       {"%q", '\U000c2345', `'\U000c2345'`},         // Rune is not printable.
+       {"%q", '\U0010ffff', `'\U0010ffff'`},         // Rune is not printable.
+       {"%q", rune(0x110000), `%!q(int32=1114112)`}, // Rune is not valid.
        {"%q", int64(0x7FFFFFFF), `%!q(int64=2147483647)`},
        {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`},
-       {"%q", '"', `'"'`},
-       {"%q", '\'', `'\''`},
-       {"%q", "\u263a", `"☺"`},
-       {"%+q", "\u263a", `"\u263a"`},
 
        // width
        {"%5s", "abc", "  abc"},
@@ -1041,6 +1100,14 @@ func BenchmarkSprintfTruncateString(b *testing.B) {
        })
 }
 
+func BenchmarkSprintfQuoteString(b *testing.B) {
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       Sprintf("%q", "日本語日本語日本語")
+               }
+       })
+}
+
 func BenchmarkSprintfInt(b *testing.B) {
        b.RunParallel(func(pb *testing.PB) {
                for pb.Next() {
index 821c7b44b1ad63f1c886bf167505716edaaec39a..417d74cd8f156b64c2dcfb36a26fa720c3a380b3 100644 (file)
@@ -378,31 +378,31 @@ func (f *fmt) fmt_bx(b []byte, digits string) {
 }
 
 // fmt_q formats a string as a double-quoted, escaped Go string constant.
+// If f.sharp is set a raw (backquoted) string may be returned instead
+// if the string does not contain any control characters other than tab.
 func (f *fmt) fmt_q(s string) {
        s = f.truncate(s)
-       var quoted string
        if f.sharp && strconv.CanBackquote(s) {
-               quoted = "`" + s + "`"
+               f.padString("`" + s + "`")
+               return
+       }
+       buf := f.intbuf[:0]
+       if f.plus {
+               f.pad(strconv.AppendQuoteToASCII(buf, s))
        } else {
-               if f.plus {
-                       quoted = strconv.QuoteToASCII(s)
-               } else {
-                       quoted = strconv.Quote(s)
-               }
+               f.pad(strconv.AppendQuote(buf, s))
        }
-       f.padString(quoted)
 }
 
 // fmt_qc formats the integer as a single-quoted, escaped Go character constant.
 // If the character is not valid Unicode, it will print '\ufffd'.
 func (f *fmt) fmt_qc(c int64) {
-       var quoted []byte
+       buf := f.intbuf[:0]
        if f.plus {
-               quoted = strconv.AppendQuoteRuneToASCII(f.intbuf[0:0], rune(c))
+               f.pad(strconv.AppendQuoteRuneToASCII(buf, rune(c)))
        } else {
-               quoted = strconv.AppendQuoteRune(f.intbuf[0:0], rune(c))
+               f.pad(strconv.AppendQuoteRune(buf, rune(c)))
        }
-       f.pad(quoted)
 }
 
 // floating-point