]> Cypherpunks repositories - gostls13.git/commitdiff
net: efficient text processing
authorRui Ueyama <ruiu@google.com>
Thu, 12 Jun 2014 03:40:00 +0000 (20:40 -0700)
committerRui Ueyama <ruiu@google.com>
Thu, 12 Jun 2014 03:40:00 +0000 (20:40 -0700)
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

src/pkg/net/ip.go
src/pkg/net/ip_test.go
src/pkg/net/parse.go

index 0582009b8bdb58169dff74f5e5131fee0075364f..4a93e97b39d30217938d979d6506a651c3d09c4d 100644 (file)
@@ -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 "<nil>"
        }
-       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,
index ffeb9d315e703936f0f5c8b43e2579d9e431a4ba..485ff51153bc484dcd0051ac91afdf8ee24ddac2 100644 (file)
@@ -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
index ee6e7e99522a2cc04346e46390d022bfc7f8451b..e1d0130c9ac7ebb925a4c78d374da84edcb337c4 100644 (file)
@@ -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.