Commit 754f0402 authored by Kyle Consalus's avatar Kyle Consalus Committed by Rob Pike

Made format string handling more efficient.

R=rsc, r, r2
CC=golang-dev
https://golang.org/cl/3421042
parent 29d0f02b
...@@ -45,11 +45,6 @@ func TestFmtInterface(t *testing.T) { ...@@ -45,11 +45,6 @@ func TestFmtInterface(t *testing.T) {
} }
} }
type fmtTest struct {
fmt string
val interface{}
out string
}
const b32 uint32 = 1<<32 - 1 const b32 uint32 = 1<<32 - 1
const b64 uint64 = 1<<64 - 1 const b64 uint64 = 1<<64 - 1
...@@ -106,7 +101,11 @@ func (p *P) String() string { ...@@ -106,7 +101,11 @@ func (p *P) String() string {
var b byte var b byte
var fmttests = []fmtTest{ var fmttests = []struct {
fmt string
val interface{}
out string
}{
{"%d", 12345, "12345"}, {"%d", 12345, "12345"},
{"%v", 12345, "12345"}, {"%v", 12345, "12345"},
{"%t", true, "true"}, {"%t", true, "true"},
...@@ -439,6 +438,12 @@ func BenchmarkSprintfIntInt(b *testing.B) { ...@@ -439,6 +438,12 @@ func BenchmarkSprintfIntInt(b *testing.B) {
} }
} }
func BenchmarkSprintfPrefixedInt(b *testing.B) {
for i := 0; i < b.N; i++ {
Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
}
}
func TestCountMallocs(t *testing.T) { func TestCountMallocs(t *testing.T) {
mallocs := 0 - runtime.MemStats.Mallocs mallocs := 0 - runtime.MemStats.Mallocs
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
...@@ -485,12 +490,10 @@ func (*flagPrinter) Format(f State, c int) { ...@@ -485,12 +490,10 @@ func (*flagPrinter) Format(f State, c int) {
io.WriteString(f, "["+s+"]") io.WriteString(f, "["+s+"]")
} }
type flagTest struct { var flagtests = []struct {
in string in string
out string out string
} }{
var flagtests = []flagTest{
{"%a", "[%a]"}, {"%a", "[%a]"},
{"%-a", "[%-a]"}, {"%-a", "[%-a]"},
{"%+a", "[%+a]"}, {"%+a", "[%+a]"},
...@@ -524,11 +527,10 @@ func TestStructPrinter(t *testing.T) { ...@@ -524,11 +527,10 @@ func TestStructPrinter(t *testing.T) {
s.a = "abc" s.a = "abc"
s.b = "def" s.b = "def"
s.c = 123 s.c = 123
type Test struct { var tests = []struct {
fmt string fmt string
out string out string
} }{
var tests = []Test{
{"%v", "{abc def 123}"}, {"%v", "{abc def 123}"},
{"%+v", "{a:abc b:def c:123}"}, {"%+v", "{a:abc b:def c:123}"},
} }
...@@ -622,13 +624,11 @@ func TestFormatterPrintln(t *testing.T) { ...@@ -622,13 +624,11 @@ func TestFormatterPrintln(t *testing.T) {
func args(a ...interface{}) []interface{} { return a } func args(a ...interface{}) []interface{} { return a }
type starTest struct { var startests = []struct {
fmt string fmt string
in []interface{} in []interface{}
out string out string
} }{
var startests = []starTest{
{"%*d", args(4, 42), " 42"}, {"%*d", args(4, 42), " 42"},
{"%.*d", args(4, 42), "0042"}, {"%.*d", args(4, 42), "0042"},
{"%*.*d", args(8, 4, 42), " 0042"}, {"%*.*d", args(8, 4, 42), " 0042"},
...@@ -644,18 +644,9 @@ var startests = []starTest{ ...@@ -644,18 +644,9 @@ var startests = []starTest{
{"%*d", args(int32(4), 42), "%!(BADWIDTH)42"}, {"%*d", args(int32(4), 42), "%!(BADWIDTH)42"},
} }
// TODO: there's no conversion from []T to ...T, but we can fake it. These
// functions do the faking. We index the table by the length of the param list.
var sprintf = []func(string, []interface{}) string{
0: func(f string, i []interface{}) string { return Sprintf(f) },
1: func(f string, i []interface{}) string { return Sprintf(f, i[0]) },
2: func(f string, i []interface{}) string { return Sprintf(f, i[0], i[1]) },
3: func(f string, i []interface{}) string { return Sprintf(f, i[0], i[1], i[2]) },
}
func TestWidthAndPrecision(t *testing.T) { func TestWidthAndPrecision(t *testing.T) {
for _, tt := range startests { for _, tt := range startests {
s := sprintf[len(tt.in)](tt.fmt, tt.in) s := Sprintf(tt.fmt, tt.in...)
if s != tt.out { if s != tt.out {
t.Errorf("%q: got %q expected %q", tt.fmt, s, tt.out) t.Errorf("%q: got %q expected %q", tt.fmt, s, tt.out)
} }
......
...@@ -118,12 +118,7 @@ func (p *pp) Flag(b int) bool { ...@@ -118,12 +118,7 @@ func (p *pp) Flag(b int) bool {
} }
func (p *pp) add(c int) { func (p *pp) add(c int) {
if c < utf8.RuneSelf { p.buf.WriteRune(c)
p.buf.WriteByte(byte(c))
} else {
w := utf8.EncodeRune(p.runeBuf[0:], c)
p.buf.Write(p.runeBuf[0:w])
}
} }
// Implement Write so we can call Fprintf on a pp (through State), for // Implement Write so we can call Fprintf on a pp (through State), for
...@@ -825,12 +820,18 @@ func (p *pp) doPrintf(format string, a []interface{}) { ...@@ -825,12 +820,18 @@ func (p *pp) doPrintf(format string, a []interface{}) {
end := len(format) end := len(format)
fieldnum := 0 // we process one field per non-trivial format fieldnum := 0 // we process one field per non-trivial format
for i := 0; i < end; { for i := 0; i < end; {
c, w := utf8.DecodeRuneInString(format[i:]) lasti := i
if c != '%' { for i < end && format[i] != '%' {
p.buf.WriteRune(c) i++
i += w }
continue if i > lasti {
p.buf.WriteString(format[lasti:i])
} }
if i >= end {
// done processing format string
break
}
// Process one verb // Process one verb
i++ i++
// flags and widths // flags and widths
...@@ -876,7 +877,7 @@ func (p *pp) doPrintf(format string, a []interface{}) { ...@@ -876,7 +877,7 @@ func (p *pp) doPrintf(format string, a []interface{}) {
p.buf.Write(noVerbBytes) p.buf.Write(noVerbBytes)
continue continue
} }
c, w = utf8.DecodeRuneInString(format[i:]) c, w := utf8.DecodeRuneInString(format[i:])
i += w i += w
// percent is special - absorbs no operand // percent is special - absorbs no operand
if c == '%' { if c == '%' {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment