Commit f7c99f33 authored by Rui Ueyama's avatar Rui Ueyama

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
parent f837078c
...@@ -287,6 +287,7 @@ func (ip IP) String() string { ...@@ -287,6 +287,7 @@ func (ip IP) String() string {
if j > i && j-i > e1-e0 { if j > i && j-i > e1-e0 {
e0 = i e0 = i
e1 = j e1 = j
i = j
} }
} }
// The symbol "::" MUST NOT be used to shorten just one 16 bit 0 field. // The symbol "::" MUST NOT be used to shorten just one 16 bit 0 field.
...@@ -295,21 +296,23 @@ func (ip IP) String() string { ...@@ -295,21 +296,23 @@ func (ip IP) String() string {
e1 = -1 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 // Print with possible :: in place of run of zeros
var s string
for i := 0; i < IPv6len; i += 2 { for i := 0; i < IPv6len; i += 2 {
if i == e0 { if i == e0 {
s += "::" b = append(b, ':', ':')
i = e1 i = e1
if i >= IPv6len { if i >= IPv6len {
break break
} }
} else if i > 0 { } 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 // ipEmptyString is like ip.String except that it returns
...@@ -419,14 +422,14 @@ func (m IPMask) Size() (ones, bits int) { ...@@ -419,14 +422,14 @@ func (m IPMask) Size() (ones, bits int) {
// String returns the hexadecimal form of m, with no punctuation. // String returns the hexadecimal form of m, with no punctuation.
func (m IPMask) String() string { func (m IPMask) String() string {
s := "" if len(m) == 0 {
for _, b := range m {
s += itox(uint(b), 2)
}
if len(s) == 0 {
return "<nil>" 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) { func networkNumberAndMask(n *IPNet) (ip IP, m IPMask) {
...@@ -646,11 +649,16 @@ func (e *ParseError) Error() string { ...@@ -646,11 +649,16 @@ func (e *ParseError) Error() string {
// If s is not a valid textual representation of an IP address, // If s is not a valid textual representation of an IP address,
// ParseIP returns nil. // ParseIP returns nil.
func ParseIP(s string) IP { func ParseIP(s string) IP {
if ip := parseIPv4(s); ip != nil { for i := 0; i < len(s); i++ {
return ip switch s[i] {
case '.':
return parseIPv4(s)
case ':':
ip, _ := parseIPv6(s, false)
return ip
}
} }
ip, _ := parseIPv6(s, false) return nil
return ip
} }
// ParseCIDR parses s as a CIDR notation IP address and mask, // ParseCIDR parses s as a CIDR notation IP address and mask,
......
...@@ -44,6 +44,14 @@ func TestParseIP(t *testing.T) { ...@@ -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 // Issue 6339
func TestMarshalEmptyIP(t *testing.T) { func TestMarshalEmptyIP(t *testing.T) {
for _, in := range [][]byte{nil, []byte("")} { for _, in := range [][]byte{nil, []byte("")} {
...@@ -91,6 +99,16 @@ func TestIPString(t *testing.T) { ...@@ -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 { var ipMaskTests = []struct {
in IP in IP
mask IPMask mask IPMask
...@@ -131,6 +149,14 @@ func TestIPMaskString(t *testing.T) { ...@@ -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 { var parseCIDRTests = []struct {
in string in string
ip IP ip IP
......
...@@ -210,18 +210,18 @@ func itod(i uint) string { ...@@ -210,18 +210,18 @@ func itod(i uint) string {
return string(b[bp:]) return string(b[bp:])
} }
// Convert i to hexadecimal string. // Convert i to a hexadecimal string. Leading zeros are not printed.
func itox(i uint, min int) string { func appendHex(dst []byte, i uint32) []byte {
// Assemble hexadecimal in reverse order. if i == 0 {
var b [32]byte return append(dst, '0')
bp := len(b)
for ; i > 0 || min > 0; i /= 16 {
bp--
b[bp] = "0123456789abcdef"[byte(i%16)]
min--
} }
for j := 7; j >= 0; j-- {
return string(b[bp:]) v := i >> uint(j*4)
if v > 0 {
dst = append(dst, hexDigit[v&0xf])
}
}
return dst
} }
// Number of occurrences of b in s. // Number of occurrences of b in s.
......
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