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 {
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,
......
......@@ -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
......
......@@ -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.
......
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