net/url: use strings.IndexByte instead of strings.Index in split function

Production profiling shows ~15% of url.Parse time being spend in the overhead
of calling strings.IndexByte through strings.Index instead of calling
strings.IndexByte directly.

name   old time/op  new time/op  delta
Split  15.5ns ± 2%  10.7ns ± 3%  -30.98%  (p=0.000 n=20+19)

Change-Id: Ie25dd4afa93539a1335a91ab2a4a367f97bd3df0
Reviewed-on: https://go-review.googlesource.com/c/go/+/178877
Run-TryBot: Martin Möhrmann <moehrmann@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/net/url/url.go b/src/net/url/url.go
index 12ea35f..504f553 100644
--- a/src/net/url/url.go
+++ b/src/net/url/url.go
@@ -449,16 +449,16 @@
 	return "", rawurl, nil
 }
 
-// Maybe s is of the form t c u.
-// If so, return t, c u (or t, u if cutc == true).
-// If not, return s, "".
-func split(s string, c string, cutc bool) (string, string) {
-	i := strings.Index(s, c)
+// split slices s into two substrings separated by the first occurence of
+// sep. If cutc is true then sep is included with the second substring.
+// If sep does not occur in s then s and the empty string is returned.
+func split(s string, sep byte, cutc bool) (string, string) {
+	i := strings.IndexByte(s, sep)
 	if i < 0 {
 		return s, ""
 	}
 	if cutc {
-		return s[:i], s[i+len(c):]
+		return s[:i], s[i+1:]
 	}
 	return s[:i], s[i:]
 }
@@ -471,7 +471,7 @@
 // error, due to parsing ambiguities.
 func Parse(rawurl string) (*URL, error) {
 	// Cut off #frag
-	u, frag := split(rawurl, "#", true)
+	u, frag := split(rawurl, '#', true)
 	url, err := parse(u, false)
 	if err != nil {
 		return nil, &Error{"parse", u, err}
@@ -531,7 +531,7 @@
 		url.ForceQuery = true
 		rest = rest[:len(rest)-1]
 	} else {
-		rest, url.RawQuery = split(rest, "?", true)
+		rest, url.RawQuery = split(rest, '?', true)
 	}
 
 	if !strings.HasPrefix(rest, "/") {
@@ -560,7 +560,7 @@
 
 	if (url.Scheme != "" || !viaRequest && !strings.HasPrefix(rest, "///")) && strings.HasPrefix(rest, "//") {
 		var authority string
-		authority, rest = split(rest[2:], "/", false)
+		authority, rest = split(rest[2:], '/', false)
 		url.User, url.Host, err = parseAuthority(authority)
 		if err != nil {
 			return nil, err
@@ -599,7 +599,7 @@
 		}
 		user = User(userinfo)
 	} else {
-		username, password := split(userinfo, ":", true)
+		username, password := split(userinfo, ':', true)
 		if username, err = unescape(username, encodeUserPassword); err != nil {
 			return nil, "", err
 		}
diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go
index c4875bb..b2f9746 100644
--- a/src/net/url/url_test.go
+++ b/src/net/url/url_test.go
@@ -1874,3 +1874,12 @@
 		})
 	}
 }
+
+var sink string
+
+func BenchmarkSplit(b *testing.B) {
+	url := "http://www.google.com/?q=go+language#foo%26bar"
+	for i := 0; i < b.N; i++ {
+		sink, sink = split(url, '#', true)
+	}
+}