Commit da82dfac authored by Andrew Gerrand's avatar Andrew Gerrand

net/url: use bytes.Buffer in (*URL).String

BenchmarkString before:

        11990 ns/op            1621 B/op         73 allocs/op

Using bytes.Buffer:

        8774 ns/op            1994 B/op         40 allocs/op

I also tried making a version of escape() that writes directly to the
bytes.Buffer, but it only saved 1 alloc/op and increased CPU time by
about 10%. Didn't seem worth the extra code path.

R=bradfitz
CC=golang-dev
https://golang.org/cl/7182050
parent cdd6ae12
...@@ -434,32 +434,35 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) { ...@@ -434,32 +434,35 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
// String reassembles the URL into a valid URL string. // String reassembles the URL into a valid URL string.
func (u *URL) String() string { func (u *URL) String() string {
// TODO: Rewrite to use bytes.Buffer var buf bytes.Buffer
result := ""
if u.Scheme != "" { if u.Scheme != "" {
result += u.Scheme + ":" buf.WriteString(u.Scheme)
buf.WriteByte(':')
} }
if u.Opaque != "" { if u.Opaque != "" {
result += u.Opaque buf.WriteString(u.Opaque)
} else { } else {
if u.Scheme != "" || u.Host != "" || u.User != nil { if u.Scheme != "" || u.Host != "" || u.User != nil {
result += "//" buf.WriteString("//")
if u := u.User; u != nil { if u := u.User; u != nil {
result += u.String() + "@" buf.WriteString(u.String())
buf.WriteByte('@')
} }
if h := u.Host; h != "" { if h := u.Host; h != "" {
result += u.Host buf.WriteString(h)
} }
} }
result += escape(u.Path, encodePath) buf.WriteString(escape(u.Path, encodePath))
} }
if u.RawQuery != "" { if u.RawQuery != "" {
result += "?" + u.RawQuery buf.WriteByte('?')
buf.WriteString(u.RawQuery)
} }
if u.Fragment != "" { if u.Fragment != "" {
result += "#" + escape(u.Fragment, encodeFragment) buf.WriteByte('#')
buf.WriteString(escape(u.Fragment, encodeFragment))
} }
return result return buf.String()
} }
// Values maps a string key to a list of values. // Values maps a string key to a list of values.
......
...@@ -280,6 +280,30 @@ func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests [ ...@@ -280,6 +280,30 @@ func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests [
} }
} }
func BenchmarkString(b *testing.B) {
b.StopTimer()
b.ReportAllocs()
for _, tt := range urltests {
u, err := Parse(tt.in)
if err != nil {
b.Errorf("Parse(%q) returned error %s", tt.in, err)
continue
}
if tt.roundtrip == "" {
continue
}
b.StartTimer()
var g string
for i := 0; i < b.N; i++ {
g = u.String()
}
b.StopTimer()
if w := tt.roundtrip; g != w {
b.Errorf("Parse(%q).String() == %q, want %q", tt.in, g, w)
}
}
}
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
DoTest(t, Parse, "Parse", urltests) DoTest(t, Parse, "Parse", urltests)
} }
......
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