From: Rui Ueyama Date: Thu, 12 Jun 2014 03:40:00 +0000 (-0700) Subject: net: efficient text processing X-Git-Tag: go1.4beta1~1309 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f7c99f3377b2d75a5fd7913e04034fb0741edf82;p=gostls13.git net: efficient text processing Optimize IP.String, IPMask.String and ParseIP. benchmark old ns/op new ns/op delta BenchmarkParseIP 2216 1849 -16.56% BenchmarkIPString 7828 2486 -68.24% BenchmarkIPMaskString 3872 659 -82.98% LGTM=mikioh.mikioh, dave, bradfitz R=golang-codereviews, mikioh.mikioh, dave, bradfitz CC=golang-codereviews https://golang.org/cl/95750043 --- diff --git a/src/pkg/net/ip.go b/src/pkg/net/ip.go index 0582009b8b..4a93e97b39 100644 --- a/src/pkg/net/ip.go +++ b/src/pkg/net/ip.go @@ -287,6 +287,7 @@ func (ip IP) String() string { if j > i && j-i > e1-e0 { e0 = i e1 = j + i = j } } // The symbol "::" MUST NOT be used to shorten just one 16 bit 0 field. @@ -295,21 +296,23 @@ func (ip IP) String() string { e1 = -1 } + const maxLen = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") + b := make([]byte, 0, maxLen) + // Print with possible :: in place of run of zeros - var s string for i := 0; i < IPv6len; i += 2 { if i == e0 { - s += "::" + b = append(b, ':', ':') i = e1 if i >= IPv6len { break } } else if i > 0 { - s += ":" + b = append(b, ':') } - s += itox((uint(p[i])<<8)|uint(p[i+1]), 1) + b = appendHex(b, (uint32(p[i])<<8)|uint32(p[i+1])) } - return s + return string(b) } // ipEmptyString is like ip.String except that it returns @@ -419,14 +422,14 @@ func (m IPMask) Size() (ones, bits int) { // String returns the hexadecimal form of m, with no punctuation. func (m IPMask) String() string { - s := "" - for _, b := range m { - s += itox(uint(b), 2) - } - if len(s) == 0 { + if len(m) == 0 { return "" } - return s + buf := make([]byte, len(m)*2) + for i, b := range m { + buf[i*2], buf[i*2+1] = hexDigit[b>>4], hexDigit[b&0xf] + } + return string(buf) } func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) { @@ -646,11 +649,16 @@ func (e *ParseError) Error() string { // If s is not a valid textual representation of an IP address, // ParseIP returns nil. func ParseIP(s string) IP { - if ip := parseIPv4(s); ip != nil { - return ip + for i := 0; i < len(s); i++ { + switch s[i] { + case '.': + return parseIPv4(s) + case ':': + ip, _ := parseIPv6(s, false) + return ip + } } - ip, _ := parseIPv6(s, false) - return ip + return nil } // ParseCIDR parses s as a CIDR notation IP address and mask, diff --git a/src/pkg/net/ip_test.go b/src/pkg/net/ip_test.go index ffeb9d315e..485ff51153 100644 --- a/src/pkg/net/ip_test.go +++ b/src/pkg/net/ip_test.go @@ -44,6 +44,14 @@ func TestParseIP(t *testing.T) { } } +func BenchmarkParseIP(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tt := range parseIPTests { + ParseIP(tt.in) + } + } +} + // Issue 6339 func TestMarshalEmptyIP(t *testing.T) { for _, in := range [][]byte{nil, []byte("")} { @@ -91,6 +99,16 @@ func TestIPString(t *testing.T) { } } +func BenchmarkIPString(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tt := range ipStringTests { + if tt.in != nil { + tt.in.String() + } + } + } +} + var ipMaskTests = []struct { in IP mask IPMask @@ -131,6 +149,14 @@ func TestIPMaskString(t *testing.T) { } } +func BenchmarkIPMaskString(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, tt := range ipMaskStringTests { + tt.in.String() + } + } +} + var parseCIDRTests = []struct { in string ip IP diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go index ee6e7e9952..e1d0130c9a 100644 --- a/src/pkg/net/parse.go +++ b/src/pkg/net/parse.go @@ -210,18 +210,18 @@ func itod(i uint) string { return string(b[bp:]) } -// Convert i to hexadecimal string. -func itox(i uint, min int) string { - // Assemble hexadecimal in reverse order. - var b [32]byte - bp := len(b) - for ; i > 0 || min > 0; i /= 16 { - bp-- - b[bp] = "0123456789abcdef"[byte(i%16)] - min-- +// Convert i to a hexadecimal string. Leading zeros are not printed. +func appendHex(dst []byte, i uint32) []byte { + if i == 0 { + return append(dst, '0') } - - return string(b[bp:]) + for j := 7; j >= 0; j-- { + v := i >> uint(j*4) + if v > 0 { + dst = append(dst, hexDigit[v&0xf]) + } + } + return dst } // Number of occurrences of b in s.