2009-06-04 14:41:31 -07:00
|
|
|
// Copyright 2009 The Go Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
2011-04-20 09:57:05 +10:00
|
|
|
// Package bytes implements functions for the manipulation of byte slices.
|
|
|
|
|
// It is analogous to the facilities of the strings package.
|
2009-06-04 14:41:31 -07:00
|
|
|
package bytes
|
|
|
|
|
|
2009-09-01 13:46:59 -07:00
|
|
|
import (
|
2009-12-15 15:33:31 -08:00
|
|
|
"unicode"
|
2011-11-08 15:40:58 -08:00
|
|
|
"unicode/utf8"
|
2009-09-01 13:46:59 -07:00
|
|
|
)
|
2009-06-04 14:41:31 -07:00
|
|
|
|
2011-12-07 15:09:56 -05:00
|
|
|
func equalPortable(a, b []byte) bool {
|
2009-06-04 14:41:31 -07:00
|
|
|
if len(a) != len(b) {
|
2009-11-09 12:07:39 -08:00
|
|
|
return false
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2010-05-03 10:59:00 -07:00
|
|
|
for i, c := range a {
|
|
|
|
|
if c != b[i] {
|
2009-11-09 12:07:39 -08:00
|
|
|
return false
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return true
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
|
2015-01-22 09:16:20 -08:00
|
|
|
// explode splits s into a slice of UTF-8 sequences, one per Unicode code point (still slices of bytes),
|
2013-01-07 10:48:06 +11:00
|
|
|
// up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes.
|
2009-06-24 19:02:29 -07:00
|
|
|
func explode(s []byte, n int) [][]byte {
|
|
|
|
|
if n <= 0 {
|
2009-11-09 12:07:39 -08:00
|
|
|
n = len(s)
|
2009-06-24 19:02:29 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
a := make([][]byte, n)
|
|
|
|
|
var size int
|
|
|
|
|
na := 0
|
2009-06-04 14:41:31 -07:00
|
|
|
for len(s) > 0 {
|
2009-06-24 19:02:29 -07:00
|
|
|
if na+1 >= n {
|
2009-12-15 15:33:31 -08:00
|
|
|
a[na] = s
|
|
|
|
|
na++
|
|
|
|
|
break
|
2009-06-24 19:02:29 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
_, size = utf8.DecodeRune(s)
|
|
|
|
|
a[na] = s[0:size]
|
|
|
|
|
s = s[size:]
|
|
|
|
|
na++
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return a[0:na]
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
|
2017-04-03 15:54:20 -07:00
|
|
|
// countGeneric actually implements Count
|
2017-03-19 12:18:08 +01:00
|
|
|
func countGeneric(s, sep []byte) int {
|
2017-02-07 16:00:39 -06:00
|
|
|
// special case
|
|
|
|
|
if len(sep) == 0 {
|
2009-11-09 12:07:39 -08:00
|
|
|
return utf8.RuneCount(s) + 1
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2017-04-03 16:08:13 -07:00
|
|
|
n := 0
|
2017-02-07 16:00:39 -06:00
|
|
|
for {
|
|
|
|
|
i := Index(s, sep)
|
|
|
|
|
if i == -1 {
|
|
|
|
|
return n
|
2011-12-07 15:09:56 -05:00
|
|
|
}
|
2017-02-07 16:00:39 -06:00
|
|
|
n++
|
|
|
|
|
s = s[i+len(sep):]
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-23 11:59:49 +10:00
|
|
|
// Contains reports whether subslice is within b.
|
2011-11-04 17:55:21 +11:00
|
|
|
func Contains(b, subslice []byte) bool {
|
2011-11-04 17:46:52 +11:00
|
|
|
return Index(b, subslice) != -1
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-05 15:43:07 -07:00
|
|
|
// ContainsAny reports whether any of the UTF-8-encoded Unicode code points in chars are within b.
|
|
|
|
|
func ContainsAny(b []byte, chars string) bool {
|
|
|
|
|
return IndexAny(b, chars) >= 0
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-07 23:22:30 -07:00
|
|
|
// ContainsRune reports whether the Unicode code point r is within b.
|
|
|
|
|
func ContainsRune(b []byte, r rune) bool {
|
|
|
|
|
return IndexRune(b, r) >= 0
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-04 10:23:43 -08:00
|
|
|
func indexBytePortable(s []byte, c byte) int {
|
2009-11-18 19:23:08 -08:00
|
|
|
for i, b := range s {
|
|
|
|
|
if b == c {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return -1
|
2009-11-18 19:23:08 -08:00
|
|
|
}
|
|
|
|
|
|
2009-10-12 10:09:35 -07:00
|
|
|
// LastIndex returns the index of the last instance of sep in s, or -1 if sep is not present in s.
|
|
|
|
|
func LastIndex(s, sep []byte) int {
|
2009-12-15 15:33:31 -08:00
|
|
|
n := len(sep)
|
2009-10-12 10:09:35 -07:00
|
|
|
if n == 0 {
|
2009-11-09 12:07:39 -08:00
|
|
|
return len(s)
|
2009-10-12 10:09:35 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
c := sep[0]
|
2009-11-09 21:09:34 -08:00
|
|
|
for i := len(s) - n; i >= 0; i-- {
|
|
|
|
|
if s[i] == c && (n == 1 || Equal(s[i:i+n], sep)) {
|
2009-11-09 12:07:39 -08:00
|
|
|
return i
|
2009-10-12 10:09:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return -1
|
2009-10-12 10:09:35 -07:00
|
|
|
}
|
|
|
|
|
|
2015-04-29 20:45:55 +03:00
|
|
|
// LastIndexByte returns the index of the last instance of c in s, or -1 if c is not present in s.
|
|
|
|
|
func LastIndexByte(s []byte, c byte) int {
|
|
|
|
|
for i := len(s) - 1; i >= 0; i-- {
|
|
|
|
|
if s[i] == c {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-05 23:11:06 +10:00
|
|
|
// IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points.
|
|
|
|
|
// It returns the byte index of the first occurrence in s of the given rune.
|
|
|
|
|
// It returns -1 if rune is not present in s.
|
2016-10-26 14:18:37 -07:00
|
|
|
// If r is utf8.RuneError, it returns the first instance of any
|
|
|
|
|
// invalid UTF-8 byte sequence.
|
2011-10-25 22:22:09 -07:00
|
|
|
func IndexRune(s []byte, r rune) int {
|
2016-10-26 14:18:37 -07:00
|
|
|
switch {
|
|
|
|
|
case 0 <= r && r < utf8.RuneSelf:
|
2016-09-06 08:09:27 +09:00
|
|
|
return IndexByte(s, byte(r))
|
2016-10-26 14:18:37 -07:00
|
|
|
case r == utf8.RuneError:
|
|
|
|
|
for i := 0; i < len(s); {
|
|
|
|
|
r1, n := utf8.DecodeRune(s[i:])
|
|
|
|
|
if r1 == utf8.RuneError {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
i += n
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
case !utf8.ValidRune(r):
|
|
|
|
|
return -1
|
|
|
|
|
default:
|
|
|
|
|
var b [utf8.UTFMax]byte
|
|
|
|
|
n := utf8.EncodeRune(b[:], r)
|
|
|
|
|
return Index(s, b[:n])
|
2010-08-05 23:11:06 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-23 12:34:35 -07:00
|
|
|
// IndexAny interprets s as a sequence of UTF-8-encoded Unicode code points.
|
2010-05-03 10:59:00 -07:00
|
|
|
// It returns the byte index of the first occurrence in s of any of the Unicode
|
2016-03-01 23:21:55 +00:00
|
|
|
// code points in chars. It returns -1 if chars is empty or if there is no code
|
2010-05-03 10:59:00 -07:00
|
|
|
// point in common.
|
|
|
|
|
func IndexAny(s []byte, chars string) int {
|
|
|
|
|
if len(chars) > 0 {
|
bytes, strings: optimize for ASCII sets
In a large codebase within Google, there are thousands of uses of:
ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight
An analysis of their usage shows that over 97% of them only use character
sets consisting of only ASCII symbols.
Uses of ContainsAny|IndexAny|LastIndexAny:
6% are 1 character (e.g., "\n" or " ")
58% are 2-4 characters (e.g., "<>" or "\r\n\t ")
24% are 5-9 characters (e.g., "()[]*^$")
10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ")
We optimize for ASCII sets, which are commonly used to search for
"control" characters in some string. We don't optimize for the
single character scenario since IndexRune or IndexByte could be used.
Uses of Trim|TrimLeft|TrimRight:
71% are 1 character (e.g., "\n" or " ")
14% are 2 characters (e.g., "\r\n")
10% are 3-4 characters (e.g., " \t\r\n")
5% are 10+ characters (e.g., "0123456789abcdefABCDEF")
We optimize for the single character case with a simple closured function
that only checks for that character's value. We optimize for the medium
and larger sets using a 16-byte bit-map representing a set of ASCII characters.
The benchmarks below have the following suffix name "%d:%d" where the first
number is the length of the input and the second number is the length
of the charset.
== bytes package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75%
BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69%
BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88%
BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91%
BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71%
BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56%
BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67%
BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21%
BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15%
BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79%
BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41%
BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82%
BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55%
BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24%
BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31%
BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08%
BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92%
BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99%
BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68%
BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27%
BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94%
BenchmarkTrimASCII/1:2-4 79.0 105 +32.91%
BenchmarkTrimASCII/1:4-4 79.6 109 +36.93%
BenchmarkTrimASCII/1:8-4 78.8 118 +49.75%
BenchmarkTrimASCII/1:16-4 80.2 132 +64.59%
BenchmarkTrimASCII/16:1-4 243 116 -52.26%
BenchmarkTrimASCII/16:2-4 243 171 -29.63%
BenchmarkTrimASCII/16:4-4 243 176 -27.57%
BenchmarkTrimASCII/16:8-4 241 184 -23.65%
BenchmarkTrimASCII/16:16-4 238 199 -16.39%
BenchmarkTrimASCII/256:1-4 2580 840 -67.44%
BenchmarkTrimASCII/256:2-4 2603 1175 -54.86%
BenchmarkTrimASCII/256:4-4 2572 1188 -53.81%
BenchmarkTrimASCII/256:8-4 2550 1191 -53.29%
BenchmarkTrimASCII/256:16-4 2585 1208 -53.27%
BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37%
BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86%
BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66%
BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88%
BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21%
== strings package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33%
BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57%
BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22%
BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85%
BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15%
BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54%
BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24%
BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10%
BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03%
BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48%
BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74%
BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08%
BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37%
BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44%
BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21%
BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95%
BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19%
BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25%
BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37%
BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26%
BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14%
BenchmarkTrimASCII/1:2-4 73.3 103 +40.52%
BenchmarkTrimASCII/1:4-4 71.8 106 +47.63%
BenchmarkTrimASCII/1:8-4 71.2 113 +58.71%
BenchmarkTrimASCII/1:16-4 71.6 128 +78.77%
BenchmarkTrimASCII/16:1-4 152 116 -23.68%
BenchmarkTrimASCII/16:2-4 160 168 +5.00%
BenchmarkTrimASCII/16:4-4 172 170 -1.16%
BenchmarkTrimASCII/16:8-4 200 177 -11.50%
BenchmarkTrimASCII/16:16-4 254 193 -24.02%
BenchmarkTrimASCII/256:1-4 1438 864 -39.92%
BenchmarkTrimASCII/256:2-4 1551 1195 -22.95%
BenchmarkTrimASCII/256:4-4 1770 1200 -32.20%
BenchmarkTrimASCII/256:8-4 2195 1216 -44.60%
BenchmarkTrimASCII/256:16-4 3054 1224 -59.92%
BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20%
BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77%
BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90%
BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81%
BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68%
The benchmarks added test the worst case. For IndexAny, that is when the
charset matches none of the input. For Trim, it is when the charset matches
all of the input.
Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea
Reviewed-on: https://go-review.googlesource.com/31593
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
|
|
|
if len(s) > 8 {
|
|
|
|
|
if as, isASCII := makeASCIISet(chars); isASCII {
|
|
|
|
|
for i, c := range s {
|
|
|
|
|
if as.contains(c) {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-10-25 22:22:09 -07:00
|
|
|
var width int
|
2010-05-03 10:59:00 -07:00
|
|
|
for i := 0; i < len(s); i += width {
|
bytes, strings: optimize for ASCII sets
In a large codebase within Google, there are thousands of uses of:
ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight
An analysis of their usage shows that over 97% of them only use character
sets consisting of only ASCII symbols.
Uses of ContainsAny|IndexAny|LastIndexAny:
6% are 1 character (e.g., "\n" or " ")
58% are 2-4 characters (e.g., "<>" or "\r\n\t ")
24% are 5-9 characters (e.g., "()[]*^$")
10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ")
We optimize for ASCII sets, which are commonly used to search for
"control" characters in some string. We don't optimize for the
single character scenario since IndexRune or IndexByte could be used.
Uses of Trim|TrimLeft|TrimRight:
71% are 1 character (e.g., "\n" or " ")
14% are 2 characters (e.g., "\r\n")
10% are 3-4 characters (e.g., " \t\r\n")
5% are 10+ characters (e.g., "0123456789abcdefABCDEF")
We optimize for the single character case with a simple closured function
that only checks for that character's value. We optimize for the medium
and larger sets using a 16-byte bit-map representing a set of ASCII characters.
The benchmarks below have the following suffix name "%d:%d" where the first
number is the length of the input and the second number is the length
of the charset.
== bytes package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75%
BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69%
BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88%
BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91%
BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71%
BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56%
BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67%
BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21%
BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15%
BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79%
BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41%
BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82%
BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55%
BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24%
BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31%
BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08%
BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92%
BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99%
BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68%
BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27%
BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94%
BenchmarkTrimASCII/1:2-4 79.0 105 +32.91%
BenchmarkTrimASCII/1:4-4 79.6 109 +36.93%
BenchmarkTrimASCII/1:8-4 78.8 118 +49.75%
BenchmarkTrimASCII/1:16-4 80.2 132 +64.59%
BenchmarkTrimASCII/16:1-4 243 116 -52.26%
BenchmarkTrimASCII/16:2-4 243 171 -29.63%
BenchmarkTrimASCII/16:4-4 243 176 -27.57%
BenchmarkTrimASCII/16:8-4 241 184 -23.65%
BenchmarkTrimASCII/16:16-4 238 199 -16.39%
BenchmarkTrimASCII/256:1-4 2580 840 -67.44%
BenchmarkTrimASCII/256:2-4 2603 1175 -54.86%
BenchmarkTrimASCII/256:4-4 2572 1188 -53.81%
BenchmarkTrimASCII/256:8-4 2550 1191 -53.29%
BenchmarkTrimASCII/256:16-4 2585 1208 -53.27%
BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37%
BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86%
BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66%
BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88%
BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21%
== strings package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33%
BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57%
BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22%
BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85%
BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15%
BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54%
BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24%
BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10%
BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03%
BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48%
BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74%
BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08%
BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37%
BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44%
BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21%
BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95%
BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19%
BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25%
BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37%
BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26%
BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14%
BenchmarkTrimASCII/1:2-4 73.3 103 +40.52%
BenchmarkTrimASCII/1:4-4 71.8 106 +47.63%
BenchmarkTrimASCII/1:8-4 71.2 113 +58.71%
BenchmarkTrimASCII/1:16-4 71.6 128 +78.77%
BenchmarkTrimASCII/16:1-4 152 116 -23.68%
BenchmarkTrimASCII/16:2-4 160 168 +5.00%
BenchmarkTrimASCII/16:4-4 172 170 -1.16%
BenchmarkTrimASCII/16:8-4 200 177 -11.50%
BenchmarkTrimASCII/16:16-4 254 193 -24.02%
BenchmarkTrimASCII/256:1-4 1438 864 -39.92%
BenchmarkTrimASCII/256:2-4 1551 1195 -22.95%
BenchmarkTrimASCII/256:4-4 1770 1200 -32.20%
BenchmarkTrimASCII/256:8-4 2195 1216 -44.60%
BenchmarkTrimASCII/256:16-4 3054 1224 -59.92%
BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20%
BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77%
BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90%
BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81%
BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68%
The benchmarks added test the worst case. For IndexAny, that is when the
charset matches none of the input. For Trim, it is when the charset matches
all of the input.
Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea
Reviewed-on: https://go-review.googlesource.com/31593
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
|
|
|
r := rune(s[i])
|
2011-10-25 22:22:09 -07:00
|
|
|
if r < utf8.RuneSelf {
|
2010-05-03 10:59:00 -07:00
|
|
|
width = 1
|
|
|
|
|
} else {
|
2011-10-25 22:22:09 -07:00
|
|
|
r, width = utf8.DecodeRune(s[i:])
|
2010-05-03 10:59:00 -07:00
|
|
|
}
|
2011-10-25 22:22:09 -07:00
|
|
|
for _, ch := range chars {
|
|
|
|
|
if r == ch {
|
2010-03-26 13:05:04 -07:00
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-12 12:47:50 -08:00
|
|
|
// LastIndexAny interprets s as a sequence of UTF-8-encoded Unicode code
|
2016-03-01 23:21:55 +00:00
|
|
|
// points. It returns the byte index of the last occurrence in s of any of
|
|
|
|
|
// the Unicode code points in chars. It returns -1 if chars is empty or if
|
2010-11-12 12:47:50 -08:00
|
|
|
// there is no code point in common.
|
|
|
|
|
func LastIndexAny(s []byte, chars string) int {
|
|
|
|
|
if len(chars) > 0 {
|
bytes, strings: optimize for ASCII sets
In a large codebase within Google, there are thousands of uses of:
ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight
An analysis of their usage shows that over 97% of them only use character
sets consisting of only ASCII symbols.
Uses of ContainsAny|IndexAny|LastIndexAny:
6% are 1 character (e.g., "\n" or " ")
58% are 2-4 characters (e.g., "<>" or "\r\n\t ")
24% are 5-9 characters (e.g., "()[]*^$")
10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ")
We optimize for ASCII sets, which are commonly used to search for
"control" characters in some string. We don't optimize for the
single character scenario since IndexRune or IndexByte could be used.
Uses of Trim|TrimLeft|TrimRight:
71% are 1 character (e.g., "\n" or " ")
14% are 2 characters (e.g., "\r\n")
10% are 3-4 characters (e.g., " \t\r\n")
5% are 10+ characters (e.g., "0123456789abcdefABCDEF")
We optimize for the single character case with a simple closured function
that only checks for that character's value. We optimize for the medium
and larger sets using a 16-byte bit-map representing a set of ASCII characters.
The benchmarks below have the following suffix name "%d:%d" where the first
number is the length of the input and the second number is the length
of the charset.
== bytes package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75%
BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69%
BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88%
BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91%
BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71%
BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56%
BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67%
BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21%
BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15%
BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79%
BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41%
BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82%
BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55%
BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24%
BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31%
BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08%
BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92%
BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99%
BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68%
BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27%
BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94%
BenchmarkTrimASCII/1:2-4 79.0 105 +32.91%
BenchmarkTrimASCII/1:4-4 79.6 109 +36.93%
BenchmarkTrimASCII/1:8-4 78.8 118 +49.75%
BenchmarkTrimASCII/1:16-4 80.2 132 +64.59%
BenchmarkTrimASCII/16:1-4 243 116 -52.26%
BenchmarkTrimASCII/16:2-4 243 171 -29.63%
BenchmarkTrimASCII/16:4-4 243 176 -27.57%
BenchmarkTrimASCII/16:8-4 241 184 -23.65%
BenchmarkTrimASCII/16:16-4 238 199 -16.39%
BenchmarkTrimASCII/256:1-4 2580 840 -67.44%
BenchmarkTrimASCII/256:2-4 2603 1175 -54.86%
BenchmarkTrimASCII/256:4-4 2572 1188 -53.81%
BenchmarkTrimASCII/256:8-4 2550 1191 -53.29%
BenchmarkTrimASCII/256:16-4 2585 1208 -53.27%
BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37%
BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86%
BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66%
BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88%
BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21%
== strings package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33%
BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57%
BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22%
BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85%
BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15%
BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54%
BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24%
BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10%
BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03%
BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48%
BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74%
BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08%
BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37%
BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44%
BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21%
BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95%
BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19%
BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25%
BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37%
BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26%
BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14%
BenchmarkTrimASCII/1:2-4 73.3 103 +40.52%
BenchmarkTrimASCII/1:4-4 71.8 106 +47.63%
BenchmarkTrimASCII/1:8-4 71.2 113 +58.71%
BenchmarkTrimASCII/1:16-4 71.6 128 +78.77%
BenchmarkTrimASCII/16:1-4 152 116 -23.68%
BenchmarkTrimASCII/16:2-4 160 168 +5.00%
BenchmarkTrimASCII/16:4-4 172 170 -1.16%
BenchmarkTrimASCII/16:8-4 200 177 -11.50%
BenchmarkTrimASCII/16:16-4 254 193 -24.02%
BenchmarkTrimASCII/256:1-4 1438 864 -39.92%
BenchmarkTrimASCII/256:2-4 1551 1195 -22.95%
BenchmarkTrimASCII/256:4-4 1770 1200 -32.20%
BenchmarkTrimASCII/256:8-4 2195 1216 -44.60%
BenchmarkTrimASCII/256:16-4 3054 1224 -59.92%
BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20%
BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77%
BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90%
BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81%
BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68%
The benchmarks added test the worst case. For IndexAny, that is when the
charset matches none of the input. For Trim, it is when the charset matches
all of the input.
Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea
Reviewed-on: https://go-review.googlesource.com/31593
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
|
|
|
if len(s) > 8 {
|
|
|
|
|
if as, isASCII := makeASCIISet(chars); isASCII {
|
|
|
|
|
for i := len(s) - 1; i >= 0; i-- {
|
|
|
|
|
if as.contains(s[i]) {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-12 12:47:50 -08:00
|
|
|
for i := len(s); i > 0; {
|
bytes, strings: optimize for ASCII sets
In a large codebase within Google, there are thousands of uses of:
ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight
An analysis of their usage shows that over 97% of them only use character
sets consisting of only ASCII symbols.
Uses of ContainsAny|IndexAny|LastIndexAny:
6% are 1 character (e.g., "\n" or " ")
58% are 2-4 characters (e.g., "<>" or "\r\n\t ")
24% are 5-9 characters (e.g., "()[]*^$")
10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ")
We optimize for ASCII sets, which are commonly used to search for
"control" characters in some string. We don't optimize for the
single character scenario since IndexRune or IndexByte could be used.
Uses of Trim|TrimLeft|TrimRight:
71% are 1 character (e.g., "\n" or " ")
14% are 2 characters (e.g., "\r\n")
10% are 3-4 characters (e.g., " \t\r\n")
5% are 10+ characters (e.g., "0123456789abcdefABCDEF")
We optimize for the single character case with a simple closured function
that only checks for that character's value. We optimize for the medium
and larger sets using a 16-byte bit-map representing a set of ASCII characters.
The benchmarks below have the following suffix name "%d:%d" where the first
number is the length of the input and the second number is the length
of the charset.
== bytes package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75%
BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69%
BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88%
BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91%
BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71%
BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56%
BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67%
BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21%
BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15%
BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79%
BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41%
BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82%
BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55%
BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24%
BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31%
BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08%
BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92%
BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99%
BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68%
BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27%
BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94%
BenchmarkTrimASCII/1:2-4 79.0 105 +32.91%
BenchmarkTrimASCII/1:4-4 79.6 109 +36.93%
BenchmarkTrimASCII/1:8-4 78.8 118 +49.75%
BenchmarkTrimASCII/1:16-4 80.2 132 +64.59%
BenchmarkTrimASCII/16:1-4 243 116 -52.26%
BenchmarkTrimASCII/16:2-4 243 171 -29.63%
BenchmarkTrimASCII/16:4-4 243 176 -27.57%
BenchmarkTrimASCII/16:8-4 241 184 -23.65%
BenchmarkTrimASCII/16:16-4 238 199 -16.39%
BenchmarkTrimASCII/256:1-4 2580 840 -67.44%
BenchmarkTrimASCII/256:2-4 2603 1175 -54.86%
BenchmarkTrimASCII/256:4-4 2572 1188 -53.81%
BenchmarkTrimASCII/256:8-4 2550 1191 -53.29%
BenchmarkTrimASCII/256:16-4 2585 1208 -53.27%
BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37%
BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86%
BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66%
BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88%
BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21%
== strings package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33%
BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57%
BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22%
BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85%
BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15%
BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54%
BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24%
BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10%
BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03%
BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48%
BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74%
BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08%
BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37%
BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44%
BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21%
BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95%
BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19%
BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25%
BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37%
BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26%
BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14%
BenchmarkTrimASCII/1:2-4 73.3 103 +40.52%
BenchmarkTrimASCII/1:4-4 71.8 106 +47.63%
BenchmarkTrimASCII/1:8-4 71.2 113 +58.71%
BenchmarkTrimASCII/1:16-4 71.6 128 +78.77%
BenchmarkTrimASCII/16:1-4 152 116 -23.68%
BenchmarkTrimASCII/16:2-4 160 168 +5.00%
BenchmarkTrimASCII/16:4-4 172 170 -1.16%
BenchmarkTrimASCII/16:8-4 200 177 -11.50%
BenchmarkTrimASCII/16:16-4 254 193 -24.02%
BenchmarkTrimASCII/256:1-4 1438 864 -39.92%
BenchmarkTrimASCII/256:2-4 1551 1195 -22.95%
BenchmarkTrimASCII/256:4-4 1770 1200 -32.20%
BenchmarkTrimASCII/256:8-4 2195 1216 -44.60%
BenchmarkTrimASCII/256:16-4 3054 1224 -59.92%
BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20%
BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77%
BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90%
BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81%
BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68%
The benchmarks added test the worst case. For IndexAny, that is when the
charset matches none of the input. For Trim, it is when the charset matches
all of the input.
Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea
Reviewed-on: https://go-review.googlesource.com/31593
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
|
|
|
r, size := utf8.DecodeLastRune(s[:i])
|
2010-11-12 12:47:50 -08:00
|
|
|
i -= size
|
bytes, strings: optimize for ASCII sets
In a large codebase within Google, there are thousands of uses of:
ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight
An analysis of their usage shows that over 97% of them only use character
sets consisting of only ASCII symbols.
Uses of ContainsAny|IndexAny|LastIndexAny:
6% are 1 character (e.g., "\n" or " ")
58% are 2-4 characters (e.g., "<>" or "\r\n\t ")
24% are 5-9 characters (e.g., "()[]*^$")
10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ")
We optimize for ASCII sets, which are commonly used to search for
"control" characters in some string. We don't optimize for the
single character scenario since IndexRune or IndexByte could be used.
Uses of Trim|TrimLeft|TrimRight:
71% are 1 character (e.g., "\n" or " ")
14% are 2 characters (e.g., "\r\n")
10% are 3-4 characters (e.g., " \t\r\n")
5% are 10+ characters (e.g., "0123456789abcdefABCDEF")
We optimize for the single character case with a simple closured function
that only checks for that character's value. We optimize for the medium
and larger sets using a 16-byte bit-map representing a set of ASCII characters.
The benchmarks below have the following suffix name "%d:%d" where the first
number is the length of the input and the second number is the length
of the charset.
== bytes package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75%
BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69%
BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88%
BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91%
BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71%
BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56%
BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67%
BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21%
BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15%
BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79%
BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41%
BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82%
BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55%
BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24%
BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31%
BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08%
BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92%
BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99%
BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68%
BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27%
BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94%
BenchmarkTrimASCII/1:2-4 79.0 105 +32.91%
BenchmarkTrimASCII/1:4-4 79.6 109 +36.93%
BenchmarkTrimASCII/1:8-4 78.8 118 +49.75%
BenchmarkTrimASCII/1:16-4 80.2 132 +64.59%
BenchmarkTrimASCII/16:1-4 243 116 -52.26%
BenchmarkTrimASCII/16:2-4 243 171 -29.63%
BenchmarkTrimASCII/16:4-4 243 176 -27.57%
BenchmarkTrimASCII/16:8-4 241 184 -23.65%
BenchmarkTrimASCII/16:16-4 238 199 -16.39%
BenchmarkTrimASCII/256:1-4 2580 840 -67.44%
BenchmarkTrimASCII/256:2-4 2603 1175 -54.86%
BenchmarkTrimASCII/256:4-4 2572 1188 -53.81%
BenchmarkTrimASCII/256:8-4 2550 1191 -53.29%
BenchmarkTrimASCII/256:16-4 2585 1208 -53.27%
BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37%
BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86%
BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66%
BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88%
BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21%
== strings package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33%
BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57%
BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22%
BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85%
BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15%
BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54%
BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24%
BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10%
BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03%
BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48%
BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74%
BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08%
BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37%
BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44%
BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21%
BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95%
BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19%
BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25%
BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37%
BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26%
BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14%
BenchmarkTrimASCII/1:2-4 73.3 103 +40.52%
BenchmarkTrimASCII/1:4-4 71.8 106 +47.63%
BenchmarkTrimASCII/1:8-4 71.2 113 +58.71%
BenchmarkTrimASCII/1:16-4 71.6 128 +78.77%
BenchmarkTrimASCII/16:1-4 152 116 -23.68%
BenchmarkTrimASCII/16:2-4 160 168 +5.00%
BenchmarkTrimASCII/16:4-4 172 170 -1.16%
BenchmarkTrimASCII/16:8-4 200 177 -11.50%
BenchmarkTrimASCII/16:16-4 254 193 -24.02%
BenchmarkTrimASCII/256:1-4 1438 864 -39.92%
BenchmarkTrimASCII/256:2-4 1551 1195 -22.95%
BenchmarkTrimASCII/256:4-4 1770 1200 -32.20%
BenchmarkTrimASCII/256:8-4 2195 1216 -44.60%
BenchmarkTrimASCII/256:16-4 3054 1224 -59.92%
BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20%
BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77%
BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90%
BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81%
BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68%
The benchmarks added test the worst case. For IndexAny, that is when the
charset matches none of the input. For Trim, it is when the charset matches
all of the input.
Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea
Reviewed-on: https://go-review.googlesource.com/31593
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
|
|
|
for _, c := range chars {
|
|
|
|
|
if r == c {
|
2010-11-12 12:47:50 -08:00
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 15:19:30 -08:00
|
|
|
// Generic split: splits after each instance of sep,
|
2013-01-07 10:48:06 +11:00
|
|
|
// including sepSave bytes of sep in the subslices.
|
2009-11-04 15:19:30 -08:00
|
|
|
func genSplit(s, sep []byte, sepSave, n int) [][]byte {
|
2010-07-01 14:08:14 -07:00
|
|
|
if n == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2009-06-04 14:41:31 -07:00
|
|
|
if len(sep) == 0 {
|
2009-11-09 12:07:39 -08:00
|
|
|
return explode(s, n)
|
2009-06-24 19:02:29 -07:00
|
|
|
}
|
2010-07-01 14:08:14 -07:00
|
|
|
if n < 0 {
|
2009-11-09 12:07:39 -08:00
|
|
|
n = Count(s, sep) + 1
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2017-02-07 13:38:52 +02:00
|
|
|
|
2009-12-15 15:33:31 -08:00
|
|
|
a := make([][]byte, n)
|
2017-02-07 13:38:52 +02:00
|
|
|
n--
|
|
|
|
|
i := 0
|
|
|
|
|
for i < n {
|
|
|
|
|
m := Index(s, sep)
|
|
|
|
|
if m < 0 {
|
|
|
|
|
break
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2017-02-07 13:38:52 +02:00
|
|
|
a[i] = s[:m+sepSave]
|
|
|
|
|
s = s[m+len(sep):]
|
|
|
|
|
i++
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2017-02-07 13:38:52 +02:00
|
|
|
a[i] = s
|
|
|
|
|
return a[:i+1]
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
|
2011-06-28 09:43:14 +10:00
|
|
|
// SplitN slices s into subslices separated by sep and returns a slice of
|
2010-07-27 15:06:08 +10:00
|
|
|
// the subslices between those separators.
|
2011-06-28 09:43:14 +10:00
|
|
|
// If sep is empty, SplitN splits after each UTF-8 sequence.
|
2010-07-27 15:06:08 +10:00
|
|
|
// The count determines the number of subslices to return:
|
|
|
|
|
// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
|
|
|
|
|
// n == 0: the result is nil (zero subslices)
|
|
|
|
|
// n < 0: all subslices
|
2011-06-28 09:43:14 +10:00
|
|
|
func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) }
|
2009-11-04 15:19:30 -08:00
|
|
|
|
2011-06-28 09:43:14 +10:00
|
|
|
// SplitAfterN slices s into subslices after each instance of sep and
|
2010-07-27 15:06:08 +10:00
|
|
|
// returns a slice of those subslices.
|
2011-06-28 09:43:14 +10:00
|
|
|
// If sep is empty, SplitAfterN splits after each UTF-8 sequence.
|
2010-07-27 15:06:08 +10:00
|
|
|
// The count determines the number of subslices to return:
|
|
|
|
|
// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
|
|
|
|
|
// n == 0: the result is nil (zero subslices)
|
|
|
|
|
// n < 0: all subslices
|
2011-06-28 09:43:14 +10:00
|
|
|
func SplitAfterN(s, sep []byte, n int) [][]byte {
|
2009-11-09 12:07:39 -08:00
|
|
|
return genSplit(s, sep, len(sep), n)
|
2009-11-04 15:19:30 -08:00
|
|
|
}
|
|
|
|
|
|
2011-06-28 09:43:14 +10:00
|
|
|
// Split slices s into all subslices separated by sep and returns a slice of
|
|
|
|
|
// the subslices between those separators.
|
|
|
|
|
// If sep is empty, Split splits after each UTF-8 sequence.
|
|
|
|
|
// It is equivalent to SplitN with a count of -1.
|
|
|
|
|
func Split(s, sep []byte) [][]byte { return genSplit(s, sep, 0, -1) }
|
|
|
|
|
|
|
|
|
|
// SplitAfter slices s into all subslices after each instance of sep and
|
|
|
|
|
// returns a slice of those subslices.
|
|
|
|
|
// If sep is empty, SplitAfter splits after each UTF-8 sequence.
|
|
|
|
|
// It is equivalent to SplitAfterN with a count of -1.
|
|
|
|
|
func SplitAfter(s, sep []byte) [][]byte {
|
|
|
|
|
return genSplit(s, sep, len(sep), -1)
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-12 10:20:30 +02:00
|
|
|
var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
|
|
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// Fields splits the slice s around each instance of one or more consecutive white space
|
2017-08-12 10:20:30 +02:00
|
|
|
// characters, as defined by unicode.IsSpace, returning a slice of subslices of s or an
|
|
|
|
|
// empty slice if s contains only white space.
|
2009-12-15 21:09:55 -08:00
|
|
|
func Fields(s []byte) [][]byte {
|
2017-08-12 10:20:30 +02:00
|
|
|
// First count the fields.
|
|
|
|
|
// This is an exact count if s is ASCII, otherwise it is an approximation.
|
|
|
|
|
n := 0
|
|
|
|
|
wasSpace := 1
|
|
|
|
|
// setBits is used to track which bits are set in the bytes of s.
|
|
|
|
|
setBits := uint8(0)
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
r := s[i]
|
|
|
|
|
setBits |= r
|
|
|
|
|
isSpace := int(asciiSpace[r])
|
|
|
|
|
n += wasSpace & ^isSpace
|
|
|
|
|
wasSpace = isSpace
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-17 15:51:35 +01:00
|
|
|
if setBits >= utf8.RuneSelf {
|
|
|
|
|
// Some runes in the input slice are not ASCII.
|
|
|
|
|
return FieldsFunc(s, unicode.IsSpace)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ASCII fast path
|
|
|
|
|
a := make([][]byte, n)
|
|
|
|
|
na := 0
|
|
|
|
|
fieldStart := 0
|
|
|
|
|
i := 0
|
|
|
|
|
// Skip spaces in the front of the input.
|
|
|
|
|
for i < len(s) && asciiSpace[s[i]] != 0 {
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
fieldStart = i
|
|
|
|
|
for i < len(s) {
|
|
|
|
|
if asciiSpace[s[i]] == 0 {
|
2017-08-12 10:20:30 +02:00
|
|
|
i++
|
2017-08-17 15:51:35 +01:00
|
|
|
continue
|
2017-08-12 10:20:30 +02:00
|
|
|
}
|
2017-08-17 15:51:35 +01:00
|
|
|
a[na] = s[fieldStart:i]
|
|
|
|
|
na++
|
|
|
|
|
i++
|
|
|
|
|
// Skip spaces in between fields.
|
|
|
|
|
for i < len(s) && asciiSpace[s[i]] != 0 {
|
2017-08-12 10:20:30 +02:00
|
|
|
i++
|
|
|
|
|
}
|
2017-08-17 15:51:35 +01:00
|
|
|
fieldStart = i
|
2017-08-12 10:20:30 +02:00
|
|
|
}
|
2017-08-17 15:51:35 +01:00
|
|
|
if fieldStart < len(s) { // Last field might end at EOF.
|
|
|
|
|
a[na] = s[fieldStart:]
|
|
|
|
|
}
|
|
|
|
|
return a
|
2010-08-05 23:11:06 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FieldsFunc interprets s as a sequence of UTF-8-encoded Unicode code points.
|
2013-01-07 10:48:06 +11:00
|
|
|
// It splits the slice s at each run of code points c satisfying f(c) and
|
2016-03-01 23:21:55 +00:00
|
|
|
// returns a slice of subslices of s. If all code points in s satisfy f(c), or
|
2013-12-12 22:13:19 -05:00
|
|
|
// len(s) == 0, an empty slice is returned.
|
2014-09-18 19:40:31 -04:00
|
|
|
// FieldsFunc makes no guarantees about the order in which it calls f(c).
|
|
|
|
|
// If f does not return consistent results for a given c, FieldsFunc may crash.
|
2011-10-25 22:22:09 -07:00
|
|
|
func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
|
2017-08-12 10:20:30 +02:00
|
|
|
// A span is used to record a slice of s of the form s[start:end].
|
|
|
|
|
// The start index is inclusive and the end index is exclusive.
|
|
|
|
|
type span struct {
|
|
|
|
|
start int
|
|
|
|
|
end int
|
2009-12-15 21:09:55 -08:00
|
|
|
}
|
2017-08-12 10:20:30 +02:00
|
|
|
spans := make([]span, 0, 32)
|
2009-12-15 21:09:55 -08:00
|
|
|
|
2017-08-12 10:20:30 +02:00
|
|
|
// Find the field start and end indices.
|
|
|
|
|
wasField := false
|
|
|
|
|
fromIndex := 0
|
|
|
|
|
for i := 0; i < len(s); {
|
|
|
|
|
size := 1
|
|
|
|
|
r := rune(s[i])
|
|
|
|
|
if r >= utf8.RuneSelf {
|
|
|
|
|
r, size = utf8.DecodeRune(s[i:])
|
2009-12-15 21:09:55 -08:00
|
|
|
}
|
2017-08-12 10:20:30 +02:00
|
|
|
if f(r) {
|
|
|
|
|
if wasField {
|
|
|
|
|
spans = append(spans, span{start: fromIndex, end: i})
|
|
|
|
|
wasField = false
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if !wasField {
|
|
|
|
|
fromIndex = i
|
|
|
|
|
wasField = true
|
|
|
|
|
}
|
2009-12-15 21:09:55 -08:00
|
|
|
}
|
|
|
|
|
i += size
|
|
|
|
|
}
|
2017-08-12 10:20:30 +02:00
|
|
|
|
|
|
|
|
// Last field might end at EOF.
|
|
|
|
|
if wasField {
|
|
|
|
|
spans = append(spans, span{fromIndex, len(s)})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create subslices from recorded field indices.
|
|
|
|
|
a := make([][]byte, len(spans))
|
|
|
|
|
for i, span := range spans {
|
|
|
|
|
a[i] = s[span.start:span.end]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return a
|
2009-12-15 21:09:55 -08:00
|
|
|
}
|
|
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// Join concatenates the elements of s to create a new byte slice. The separator
|
|
|
|
|
// sep is placed between elements in the resulting slice.
|
|
|
|
|
func Join(s [][]byte, sep []byte) []byte {
|
|
|
|
|
if len(s) == 0 {
|
2009-11-09 12:07:39 -08:00
|
|
|
return []byte{}
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2013-01-07 10:48:06 +11:00
|
|
|
if len(s) == 1 {
|
2012-07-20 16:04:22 -03:00
|
|
|
// Just return a copy.
|
2013-01-07 10:48:06 +11:00
|
|
|
return append([]byte(nil), s[0]...)
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2013-01-07 10:48:06 +11:00
|
|
|
n := len(sep) * (len(s) - 1)
|
|
|
|
|
for _, v := range s {
|
|
|
|
|
n += len(v)
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
|
2009-12-15 15:33:31 -08:00
|
|
|
b := make([]byte, n)
|
2013-01-07 10:48:06 +11:00
|
|
|
bp := copy(b, s[0])
|
|
|
|
|
for _, v := range s[1:] {
|
2011-03-29 01:27:38 -04:00
|
|
|
bp += copy(b[bp:], sep)
|
2013-01-07 10:48:06 +11:00
|
|
|
bp += copy(b[bp:], v)
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return b
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// HasPrefix tests whether the byte slice s begins with prefix.
|
2009-06-04 14:41:31 -07:00
|
|
|
func HasPrefix(s, prefix []byte) bool {
|
2009-11-09 12:07:39 -08:00
|
|
|
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
|
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// HasSuffix tests whether the byte slice s ends with suffix.
|
2009-06-04 14:41:31 -07:00
|
|
|
func HasSuffix(s, suffix []byte) bool {
|
2009-11-20 11:45:05 -08:00
|
|
|
return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix)
|
2009-06-04 14:41:31 -07:00
|
|
|
}
|
2009-09-01 13:46:59 -07:00
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// Map returns a copy of the byte slice s with all its characters modified
|
2009-12-11 10:37:48 -08:00
|
|
|
// according to the mapping function. If mapping returns a negative value, the character is
|
2016-03-01 23:21:55 +00:00
|
|
|
// dropped from the string with no replacement. The characters in s and the
|
2010-07-23 12:34:35 -07:00
|
|
|
// output are interpreted as UTF-8-encoded Unicode code points.
|
2011-10-25 22:22:09 -07:00
|
|
|
func Map(mapping func(r rune) rune, s []byte) []byte {
|
2013-01-07 10:48:06 +11:00
|
|
|
// In the worst case, the slice can grow when mapped, making
|
2016-03-01 23:21:55 +00:00
|
|
|
// things unpleasant. But it's so rare we barge in assuming it's
|
|
|
|
|
// fine. It could also shrink but that falls out naturally.
|
2009-12-15 15:33:31 -08:00
|
|
|
maxbytes := len(s) // length of b
|
|
|
|
|
nbytes := 0 // number of bytes encoded in b
|
|
|
|
|
b := make([]byte, maxbytes)
|
2009-09-15 09:41:59 -07:00
|
|
|
for i := 0; i < len(s); {
|
2009-12-15 15:33:31 -08:00
|
|
|
wid := 1
|
2011-10-25 22:22:09 -07:00
|
|
|
r := rune(s[i])
|
|
|
|
|
if r >= utf8.RuneSelf {
|
|
|
|
|
r, wid = utf8.DecodeRune(s[i:])
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2011-10-25 22:22:09 -07:00
|
|
|
r = mapping(r)
|
|
|
|
|
if r >= 0 {
|
2014-03-18 20:52:58 -07:00
|
|
|
rl := utf8.RuneLen(r)
|
|
|
|
|
if rl < 0 {
|
|
|
|
|
rl = len(string(utf8.RuneError))
|
|
|
|
|
}
|
|
|
|
|
if nbytes+rl > maxbytes {
|
2009-12-11 10:37:48 -08:00
|
|
|
// Grow the buffer.
|
2009-12-15 15:33:31 -08:00
|
|
|
maxbytes = maxbytes*2 + utf8.UTFMax
|
|
|
|
|
nb := make([]byte, maxbytes)
|
2010-10-26 21:52:54 -07:00
|
|
|
copy(nb, b[0:nbytes])
|
2009-12-15 15:33:31 -08:00
|
|
|
b = nb
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2011-10-25 22:22:09 -07:00
|
|
|
nbytes += utf8.EncodeRune(b[nbytes:maxbytes], r)
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
i += wid
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return b[0:nbytes]
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
|
|
|
|
|
2010-05-03 10:59:00 -07:00
|
|
|
// Repeat returns a new byte slice consisting of count copies of b.
|
2016-09-28 01:54:38 -07:00
|
|
|
//
|
|
|
|
|
// It panics if count is negative or if
|
|
|
|
|
// the result of (len(b) * count) overflows.
|
2009-11-16 12:40:01 -08:00
|
|
|
func Repeat(b []byte, count int) []byte {
|
2016-09-28 01:54:38 -07:00
|
|
|
// Since we cannot return an error on overflow,
|
|
|
|
|
// we should panic if the repeat will generate
|
|
|
|
|
// an overflow.
|
|
|
|
|
// See Issue golang.org/issue/16237.
|
|
|
|
|
if count < 0 {
|
|
|
|
|
panic("bytes: negative Repeat count")
|
|
|
|
|
} else if count > 0 && len(b)*count/count != len(b) {
|
|
|
|
|
panic("bytes: Repeat count causes overflow")
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-15 15:33:31 -08:00
|
|
|
nb := make([]byte, len(b)*count)
|
2014-06-11 19:03:59 -07:00
|
|
|
bp := copy(nb, b)
|
|
|
|
|
for bp < len(nb) {
|
|
|
|
|
copy(nb[bp:], nb[:bp])
|
|
|
|
|
bp *= 2
|
2009-11-16 12:40:01 -08:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return nb
|
2009-11-16 12:40:01 -08:00
|
|
|
}
|
|
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// ToUpper returns a copy of the byte slice s with all Unicode letters mapped to their upper case.
|
2009-12-15 15:33:31 -08:00
|
|
|
func ToUpper(s []byte) []byte { return Map(unicode.ToUpper, s) }
|
2009-09-01 13:46:59 -07:00
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// ToLower returns a copy of the byte slice s with all Unicode letters mapped to their lower case.
|
2009-12-15 15:33:31 -08:00
|
|
|
func ToLower(s []byte) []byte { return Map(unicode.ToLower, s) }
|
2009-09-01 13:46:59 -07:00
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// ToTitle returns a copy of the byte slice s with all Unicode letters mapped to their title case.
|
2009-12-15 15:33:31 -08:00
|
|
|
func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) }
|
2009-09-01 13:46:59 -07:00
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// ToUpperSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
|
2010-08-05 23:11:06 +10:00
|
|
|
// upper case, giving priority to the special casing rules.
|
2016-10-26 13:20:05 -07:00
|
|
|
func ToUpperSpecial(c unicode.SpecialCase, s []byte) []byte {
|
|
|
|
|
return Map(func(r rune) rune { return c.ToUpper(r) }, s)
|
2010-08-05 23:11:06 +10:00
|
|
|
}
|
|
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// ToLowerSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
|
2010-08-05 23:11:06 +10:00
|
|
|
// lower case, giving priority to the special casing rules.
|
2016-10-26 13:20:05 -07:00
|
|
|
func ToLowerSpecial(c unicode.SpecialCase, s []byte) []byte {
|
|
|
|
|
return Map(func(r rune) rune { return c.ToLower(r) }, s)
|
2010-08-05 23:11:06 +10:00
|
|
|
}
|
|
|
|
|
|
2013-01-07 10:48:06 +11:00
|
|
|
// ToTitleSpecial returns a copy of the byte slice s with all Unicode letters mapped to their
|
2010-08-05 23:11:06 +10:00
|
|
|
// title case, giving priority to the special casing rules.
|
2016-10-26 13:20:05 -07:00
|
|
|
func ToTitleSpecial(c unicode.SpecialCase, s []byte) []byte {
|
|
|
|
|
return Map(func(r rune) rune { return c.ToTitle(r) }, s)
|
2010-08-05 23:11:06 +10:00
|
|
|
}
|
|
|
|
|
|
2010-07-20 19:53:59 -07:00
|
|
|
// isSeparator reports whether the rune could mark a word boundary.
|
|
|
|
|
// TODO: update when package unicode captures more of the properties.
|
2011-10-25 22:22:09 -07:00
|
|
|
func isSeparator(r rune) bool {
|
2010-07-20 19:53:59 -07:00
|
|
|
// ASCII alphanumerics and underscore are not separators
|
2011-10-25 22:22:09 -07:00
|
|
|
if r <= 0x7F {
|
2010-07-20 19:53:59 -07:00
|
|
|
switch {
|
2011-10-25 22:22:09 -07:00
|
|
|
case '0' <= r && r <= '9':
|
2010-07-20 19:53:59 -07:00
|
|
|
return false
|
2011-10-25 22:22:09 -07:00
|
|
|
case 'a' <= r && r <= 'z':
|
2010-07-20 19:53:59 -07:00
|
|
|
return false
|
2011-10-25 22:22:09 -07:00
|
|
|
case 'A' <= r && r <= 'Z':
|
2010-07-20 19:53:59 -07:00
|
|
|
return false
|
2011-10-25 22:22:09 -07:00
|
|
|
case r == '_':
|
2010-07-20 19:53:59 -07:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
// Letters and digits are not separators
|
2011-10-25 22:22:09 -07:00
|
|
|
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
2010-07-20 19:53:59 -07:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// Otherwise, all we can do for now is treat spaces as separators.
|
2011-10-25 22:22:09 -07:00
|
|
|
return unicode.IsSpace(r)
|
2010-07-20 19:53:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Title returns a copy of s with all Unicode letters that begin words
|
|
|
|
|
// mapped to their title case.
|
2013-03-15 17:08:07 -07:00
|
|
|
//
|
2014-12-22 18:16:15 +01:00
|
|
|
// BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly.
|
2010-07-20 19:53:59 -07:00
|
|
|
func Title(s []byte) []byte {
|
|
|
|
|
// Use a closure here to remember state.
|
|
|
|
|
// Hackish but effective. Depends on Map scanning in order and calling
|
|
|
|
|
// the closure once per rune.
|
2011-12-08 22:08:03 -05:00
|
|
|
prev := ' '
|
2010-07-20 19:53:59 -07:00
|
|
|
return Map(
|
2011-10-25 22:22:09 -07:00
|
|
|
func(r rune) rune {
|
2010-07-20 19:53:59 -07:00
|
|
|
if isSeparator(prev) {
|
|
|
|
|
prev = r
|
|
|
|
|
return unicode.ToTitle(r)
|
|
|
|
|
}
|
|
|
|
|
prev = r
|
|
|
|
|
return r
|
|
|
|
|
},
|
|
|
|
|
s)
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-23 12:34:35 -07:00
|
|
|
// TrimLeftFunc returns a subslice of s by slicing off all leading UTF-8-encoded
|
2010-05-18 23:01:05 -07:00
|
|
|
// Unicode code points c that satisfy f(c).
|
2011-10-25 22:22:09 -07:00
|
|
|
func TrimLeftFunc(s []byte, f func(r rune) bool) []byte {
|
2010-07-23 12:34:35 -07:00
|
|
|
i := indexFunc(s, f, false)
|
|
|
|
|
if i == -1 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return s[i:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimRightFunc returns a subslice of s by slicing off all trailing UTF-8
|
|
|
|
|
// encoded Unicode code points c that satisfy f(c).
|
2011-10-25 22:22:09 -07:00
|
|
|
func TrimRightFunc(s []byte, f func(r rune) bool) []byte {
|
2010-07-23 12:34:35 -07:00
|
|
|
i := lastIndexFunc(s, f, false)
|
|
|
|
|
if i >= 0 && s[i] >= utf8.RuneSelf {
|
|
|
|
|
_, wid := utf8.DecodeRune(s[i:])
|
|
|
|
|
i += wid
|
|
|
|
|
} else {
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return s[0:i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimFunc returns a subslice of s by slicing off all leading and trailing
|
|
|
|
|
// UTF-8-encoded Unicode code points c that satisfy f(c).
|
2011-10-25 22:22:09 -07:00
|
|
|
func TrimFunc(s []byte, f func(r rune) bool) []byte {
|
2010-07-23 12:34:35 -07:00
|
|
|
return TrimRightFunc(TrimLeftFunc(s, f), f)
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-01 08:41:25 -08:00
|
|
|
// TrimPrefix returns s without the provided leading prefix string.
|
|
|
|
|
// If s doesn't start with prefix, s is returned unchanged.
|
|
|
|
|
func TrimPrefix(s, prefix []byte) []byte {
|
|
|
|
|
if HasPrefix(s, prefix) {
|
|
|
|
|
return s[len(prefix):]
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimSuffix returns s without the provided trailing suffix string.
|
|
|
|
|
// If s doesn't end with suffix, s is returned unchanged.
|
|
|
|
|
func TrimSuffix(s, suffix []byte) []byte {
|
|
|
|
|
if HasSuffix(s, suffix) {
|
|
|
|
|
return s[:len(s)-len(suffix)]
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2010-07-23 12:34:35 -07:00
|
|
|
// IndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points.
|
|
|
|
|
// It returns the byte index in s of the first Unicode
|
|
|
|
|
// code point satisfying f(c), or -1 if none do.
|
2011-10-25 22:22:09 -07:00
|
|
|
func IndexFunc(s []byte, f func(r rune) bool) int {
|
2010-07-23 12:34:35 -07:00
|
|
|
return indexFunc(s, f, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LastIndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points.
|
|
|
|
|
// It returns the byte index in s of the last Unicode
|
|
|
|
|
// code point satisfying f(c), or -1 if none do.
|
2011-10-25 22:22:09 -07:00
|
|
|
func LastIndexFunc(s []byte, f func(r rune) bool) int {
|
2010-07-23 12:34:35 -07:00
|
|
|
return lastIndexFunc(s, f, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// indexFunc is the same as IndexFunc except that if
|
|
|
|
|
// truth==false, the sense of the predicate function is
|
|
|
|
|
// inverted.
|
2011-10-25 22:22:09 -07:00
|
|
|
func indexFunc(s []byte, f func(r rune) bool, truth bool) int {
|
2010-07-23 12:34:35 -07:00
|
|
|
start := 0
|
|
|
|
|
for start < len(s) {
|
|
|
|
|
wid := 1
|
2011-10-25 22:22:09 -07:00
|
|
|
r := rune(s[start])
|
|
|
|
|
if r >= utf8.RuneSelf {
|
|
|
|
|
r, wid = utf8.DecodeRune(s[start:])
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2011-10-25 22:22:09 -07:00
|
|
|
if f(r) == truth {
|
2010-07-23 12:34:35 -07:00
|
|
|
return start
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2010-07-23 12:34:35 -07:00
|
|
|
start += wid
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2010-07-23 12:34:35 -07:00
|
|
|
return -1
|
2010-05-18 23:01:05 -07:00
|
|
|
}
|
|
|
|
|
|
2010-07-23 12:34:35 -07:00
|
|
|
// lastIndexFunc is the same as LastIndexFunc except that if
|
|
|
|
|
// truth==false, the sense of the predicate function is
|
|
|
|
|
// inverted.
|
2011-10-25 22:22:09 -07:00
|
|
|
func lastIndexFunc(s []byte, f func(r rune) bool, truth bool) int {
|
2010-09-23 20:40:11 +10:00
|
|
|
for i := len(s); i > 0; {
|
2013-02-07 16:00:06 -08:00
|
|
|
r, size := rune(s[i-1]), 1
|
|
|
|
|
if r >= utf8.RuneSelf {
|
|
|
|
|
r, size = utf8.DecodeLastRune(s[0:i])
|
|
|
|
|
}
|
2010-09-23 20:40:11 +10:00
|
|
|
i -= size
|
2011-10-25 22:22:09 -07:00
|
|
|
if f(r) == truth {
|
2010-09-23 20:40:11 +10:00
|
|
|
return i
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
|
|
|
|
}
|
2010-07-23 12:34:35 -07:00
|
|
|
return -1
|
2010-05-18 23:01:05 -07:00
|
|
|
}
|
|
|
|
|
|
bytes, strings: optimize for ASCII sets
In a large codebase within Google, there are thousands of uses of:
ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight
An analysis of their usage shows that over 97% of them only use character
sets consisting of only ASCII symbols.
Uses of ContainsAny|IndexAny|LastIndexAny:
6% are 1 character (e.g., "\n" or " ")
58% are 2-4 characters (e.g., "<>" or "\r\n\t ")
24% are 5-9 characters (e.g., "()[]*^$")
10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ")
We optimize for ASCII sets, which are commonly used to search for
"control" characters in some string. We don't optimize for the
single character scenario since IndexRune or IndexByte could be used.
Uses of Trim|TrimLeft|TrimRight:
71% are 1 character (e.g., "\n" or " ")
14% are 2 characters (e.g., "\r\n")
10% are 3-4 characters (e.g., " \t\r\n")
5% are 10+ characters (e.g., "0123456789abcdefABCDEF")
We optimize for the single character case with a simple closured function
that only checks for that character's value. We optimize for the medium
and larger sets using a 16-byte bit-map representing a set of ASCII characters.
The benchmarks below have the following suffix name "%d:%d" where the first
number is the length of the input and the second number is the length
of the charset.
== bytes package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75%
BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69%
BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88%
BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91%
BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71%
BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56%
BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67%
BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21%
BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15%
BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79%
BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41%
BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82%
BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55%
BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24%
BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31%
BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08%
BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92%
BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99%
BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68%
BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27%
BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94%
BenchmarkTrimASCII/1:2-4 79.0 105 +32.91%
BenchmarkTrimASCII/1:4-4 79.6 109 +36.93%
BenchmarkTrimASCII/1:8-4 78.8 118 +49.75%
BenchmarkTrimASCII/1:16-4 80.2 132 +64.59%
BenchmarkTrimASCII/16:1-4 243 116 -52.26%
BenchmarkTrimASCII/16:2-4 243 171 -29.63%
BenchmarkTrimASCII/16:4-4 243 176 -27.57%
BenchmarkTrimASCII/16:8-4 241 184 -23.65%
BenchmarkTrimASCII/16:16-4 238 199 -16.39%
BenchmarkTrimASCII/256:1-4 2580 840 -67.44%
BenchmarkTrimASCII/256:2-4 2603 1175 -54.86%
BenchmarkTrimASCII/256:4-4 2572 1188 -53.81%
BenchmarkTrimASCII/256:8-4 2550 1191 -53.29%
BenchmarkTrimASCII/256:16-4 2585 1208 -53.27%
BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37%
BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86%
BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66%
BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88%
BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21%
== strings package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33%
BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57%
BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22%
BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85%
BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15%
BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54%
BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24%
BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10%
BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03%
BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48%
BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74%
BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08%
BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37%
BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44%
BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21%
BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95%
BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19%
BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25%
BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37%
BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26%
BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14%
BenchmarkTrimASCII/1:2-4 73.3 103 +40.52%
BenchmarkTrimASCII/1:4-4 71.8 106 +47.63%
BenchmarkTrimASCII/1:8-4 71.2 113 +58.71%
BenchmarkTrimASCII/1:16-4 71.6 128 +78.77%
BenchmarkTrimASCII/16:1-4 152 116 -23.68%
BenchmarkTrimASCII/16:2-4 160 168 +5.00%
BenchmarkTrimASCII/16:4-4 172 170 -1.16%
BenchmarkTrimASCII/16:8-4 200 177 -11.50%
BenchmarkTrimASCII/16:16-4 254 193 -24.02%
BenchmarkTrimASCII/256:1-4 1438 864 -39.92%
BenchmarkTrimASCII/256:2-4 1551 1195 -22.95%
BenchmarkTrimASCII/256:4-4 1770 1200 -32.20%
BenchmarkTrimASCII/256:8-4 2195 1216 -44.60%
BenchmarkTrimASCII/256:16-4 3054 1224 -59.92%
BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20%
BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77%
BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90%
BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81%
BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68%
The benchmarks added test the worst case. For IndexAny, that is when the
charset matches none of the input. For Trim, it is when the charset matches
all of the input.
Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea
Reviewed-on: https://go-review.googlesource.com/31593
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
|
|
|
// asciiSet is a 32-byte value, where each bit represents the presence of a
|
|
|
|
|
// given ASCII character in the set. The 128-bits of the lower 16 bytes,
|
|
|
|
|
// starting with the least-significant bit of the lowest word to the
|
|
|
|
|
// most-significant bit of the highest word, map to the full range of all
|
|
|
|
|
// 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
|
|
|
|
|
// ensuring that any non-ASCII character will be reported as not in the set.
|
|
|
|
|
type asciiSet [8]uint32
|
|
|
|
|
|
|
|
|
|
// makeASCIISet creates a set of ASCII characters and reports whether all
|
|
|
|
|
// characters in chars are ASCII.
|
|
|
|
|
func makeASCIISet(chars string) (as asciiSet, ok bool) {
|
|
|
|
|
for i := 0; i < len(chars); i++ {
|
|
|
|
|
c := chars[i]
|
|
|
|
|
if c >= utf8.RuneSelf {
|
|
|
|
|
return as, false
|
|
|
|
|
}
|
|
|
|
|
as[c>>5] |= 1 << uint(c&31)
|
|
|
|
|
}
|
|
|
|
|
return as, true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// contains reports whether c is inside the set.
|
|
|
|
|
func (as *asciiSet) contains(c byte) bool {
|
|
|
|
|
return (as[c>>5] & (1 << uint(c&31))) != 0
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-25 22:22:09 -07:00
|
|
|
func makeCutsetFunc(cutset string) func(r rune) bool {
|
bytes, strings: optimize for ASCII sets
In a large codebase within Google, there are thousands of uses of:
ContainsAny|IndexAny|LastIndexAny|Trim|TrimLeft|TrimRight
An analysis of their usage shows that over 97% of them only use character
sets consisting of only ASCII symbols.
Uses of ContainsAny|IndexAny|LastIndexAny:
6% are 1 character (e.g., "\n" or " ")
58% are 2-4 characters (e.g., "<>" or "\r\n\t ")
24% are 5-9 characters (e.g., "()[]*^$")
10% are 10+ characters (e.g., "+-=&|><!(){}[]^\"~*?:\\/ ")
We optimize for ASCII sets, which are commonly used to search for
"control" characters in some string. We don't optimize for the
single character scenario since IndexRune or IndexByte could be used.
Uses of Trim|TrimLeft|TrimRight:
71% are 1 character (e.g., "\n" or " ")
14% are 2 characters (e.g., "\r\n")
10% are 3-4 characters (e.g., " \t\r\n")
5% are 10+ characters (e.g., "0123456789abcdefABCDEF")
We optimize for the single character case with a simple closured function
that only checks for that character's value. We optimize for the medium
and larger sets using a 16-byte bit-map representing a set of ASCII characters.
The benchmarks below have the following suffix name "%d:%d" where the first
number is the length of the input and the second number is the length
of the charset.
== bytes package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.09 5.23 +2.75%
BenchmarkIndexAnyASCII/1:2-4 5.81 5.85 +0.69%
BenchmarkIndexAnyASCII/1:4-4 7.22 7.50 +3.88%
BenchmarkIndexAnyASCII/1:8-4 11.0 11.1 +0.91%
BenchmarkIndexAnyASCII/1:16-4 17.5 17.8 +1.71%
BenchmarkIndexAnyASCII/16:1-4 36.0 34.0 -5.56%
BenchmarkIndexAnyASCII/16:2-4 46.6 36.5 -21.67%
BenchmarkIndexAnyASCII/16:4-4 78.0 40.4 -48.21%
BenchmarkIndexAnyASCII/16:8-4 136 47.4 -65.15%
BenchmarkIndexAnyASCII/16:16-4 254 61.5 -75.79%
BenchmarkIndexAnyASCII/256:1-4 542 388 -28.41%
BenchmarkIndexAnyASCII/256:2-4 705 382 -45.82%
BenchmarkIndexAnyASCII/256:4-4 1089 386 -64.55%
BenchmarkIndexAnyASCII/256:8-4 1994 394 -80.24%
BenchmarkIndexAnyASCII/256:16-4 3843 411 -89.31%
BenchmarkIndexAnyASCII/4096:1-4 8522 5873 -31.08%
BenchmarkIndexAnyASCII/4096:2-4 11253 5861 -47.92%
BenchmarkIndexAnyASCII/4096:4-4 17824 5883 -66.99%
BenchmarkIndexAnyASCII/4096:8-4 32053 5871 -81.68%
BenchmarkIndexAnyASCII/4096:16-4 60512 5888 -90.27%
BenchmarkTrimASCII/1:1-4 79.5 70.8 -10.94%
BenchmarkTrimASCII/1:2-4 79.0 105 +32.91%
BenchmarkTrimASCII/1:4-4 79.6 109 +36.93%
BenchmarkTrimASCII/1:8-4 78.8 118 +49.75%
BenchmarkTrimASCII/1:16-4 80.2 132 +64.59%
BenchmarkTrimASCII/16:1-4 243 116 -52.26%
BenchmarkTrimASCII/16:2-4 243 171 -29.63%
BenchmarkTrimASCII/16:4-4 243 176 -27.57%
BenchmarkTrimASCII/16:8-4 241 184 -23.65%
BenchmarkTrimASCII/16:16-4 238 199 -16.39%
BenchmarkTrimASCII/256:1-4 2580 840 -67.44%
BenchmarkTrimASCII/256:2-4 2603 1175 -54.86%
BenchmarkTrimASCII/256:4-4 2572 1188 -53.81%
BenchmarkTrimASCII/256:8-4 2550 1191 -53.29%
BenchmarkTrimASCII/256:16-4 2585 1208 -53.27%
BenchmarkTrimASCII/4096:1-4 39773 12181 -69.37%
BenchmarkTrimASCII/4096:2-4 39946 17231 -56.86%
BenchmarkTrimASCII/4096:4-4 39641 17179 -56.66%
BenchmarkTrimASCII/4096:8-4 39835 17175 -56.88%
BenchmarkTrimASCII/4096:16-4 40229 17215 -57.21%
== strings package ==
benchmark old ns/op new ns/op delta
BenchmarkIndexAnyASCII/1:1-4 5.94 4.97 -16.33%
BenchmarkIndexAnyASCII/1:2-4 5.94 5.55 -6.57%
BenchmarkIndexAnyASCII/1:4-4 7.45 7.21 -3.22%
BenchmarkIndexAnyASCII/1:8-4 10.8 10.6 -1.85%
BenchmarkIndexAnyASCII/1:16-4 17.4 17.2 -1.15%
BenchmarkIndexAnyASCII/16:1-4 36.4 32.2 -11.54%
BenchmarkIndexAnyASCII/16:2-4 49.6 34.6 -30.24%
BenchmarkIndexAnyASCII/16:4-4 77.5 37.9 -51.10%
BenchmarkIndexAnyASCII/16:8-4 138 45.5 -67.03%
BenchmarkIndexAnyASCII/16:16-4 241 59.1 -75.48%
BenchmarkIndexAnyASCII/256:1-4 509 378 -25.74%
BenchmarkIndexAnyASCII/256:2-4 720 381 -47.08%
BenchmarkIndexAnyASCII/256:4-4 1142 384 -66.37%
BenchmarkIndexAnyASCII/256:8-4 1999 391 -80.44%
BenchmarkIndexAnyASCII/256:16-4 3735 403 -89.21%
BenchmarkIndexAnyASCII/4096:1-4 7973 5824 -26.95%
BenchmarkIndexAnyASCII/4096:2-4 11432 5809 -49.19%
BenchmarkIndexAnyASCII/4096:4-4 18327 5819 -68.25%
BenchmarkIndexAnyASCII/4096:8-4 33059 5828 -82.37%
BenchmarkIndexAnyASCII/4096:16-4 59703 5817 -90.26%
BenchmarkTrimASCII/1:1-4 71.9 71.8 -0.14%
BenchmarkTrimASCII/1:2-4 73.3 103 +40.52%
BenchmarkTrimASCII/1:4-4 71.8 106 +47.63%
BenchmarkTrimASCII/1:8-4 71.2 113 +58.71%
BenchmarkTrimASCII/1:16-4 71.6 128 +78.77%
BenchmarkTrimASCII/16:1-4 152 116 -23.68%
BenchmarkTrimASCII/16:2-4 160 168 +5.00%
BenchmarkTrimASCII/16:4-4 172 170 -1.16%
BenchmarkTrimASCII/16:8-4 200 177 -11.50%
BenchmarkTrimASCII/16:16-4 254 193 -24.02%
BenchmarkTrimASCII/256:1-4 1438 864 -39.92%
BenchmarkTrimASCII/256:2-4 1551 1195 -22.95%
BenchmarkTrimASCII/256:4-4 1770 1200 -32.20%
BenchmarkTrimASCII/256:8-4 2195 1216 -44.60%
BenchmarkTrimASCII/256:16-4 3054 1224 -59.92%
BenchmarkTrimASCII/4096:1-4 21726 12557 -42.20%
BenchmarkTrimASCII/4096:2-4 23586 17508 -25.77%
BenchmarkTrimASCII/4096:4-4 26898 17510 -34.90%
BenchmarkTrimASCII/4096:8-4 33714 17595 -47.81%
BenchmarkTrimASCII/4096:16-4 47429 17700 -62.68%
The benchmarks added test the worst case. For IndexAny, that is when the
charset matches none of the input. For Trim, it is when the charset matches
all of the input.
Change-Id: I970874d101a96b33528fc99b165379abe58cf6ea
Reviewed-on: https://go-review.googlesource.com/31593
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Martin Möhrmann <martisch@uos.de>
2016-10-20 03:16:22 -07:00
|
|
|
if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
|
|
|
|
|
return func(r rune) bool {
|
|
|
|
|
return r == rune(cutset[0])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if as, isASCII := makeASCIISet(cutset); isASCII {
|
|
|
|
|
return func(r rune) bool {
|
|
|
|
|
return r < utf8.RuneSelf && as.contains(byte(r))
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-10-25 22:22:09 -07:00
|
|
|
return func(r rune) bool {
|
2010-05-18 23:01:05 -07:00
|
|
|
for _, c := range cutset {
|
2011-10-25 22:22:09 -07:00
|
|
|
if c == r {
|
2010-05-18 23:01:05 -07:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trim returns a subslice of s by slicing off all leading and
|
2010-07-23 12:34:35 -07:00
|
|
|
// trailing UTF-8-encoded Unicode code points contained in cutset.
|
2010-05-18 23:01:05 -07:00
|
|
|
func Trim(s []byte, cutset string) []byte {
|
|
|
|
|
return TrimFunc(s, makeCutsetFunc(cutset))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimLeft returns a subslice of s by slicing off all leading
|
2010-07-23 12:34:35 -07:00
|
|
|
// UTF-8-encoded Unicode code points contained in cutset.
|
2010-05-18 23:01:05 -07:00
|
|
|
func TrimLeft(s []byte, cutset string) []byte {
|
|
|
|
|
return TrimLeftFunc(s, makeCutsetFunc(cutset))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimRight returns a subslice of s by slicing off all trailing
|
2010-07-23 12:34:35 -07:00
|
|
|
// UTF-8-encoded Unicode code points that are contained in cutset.
|
2010-05-18 23:01:05 -07:00
|
|
|
func TrimRight(s []byte, cutset string) []byte {
|
|
|
|
|
return TrimRightFunc(s, makeCutsetFunc(cutset))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimSpace returns a subslice of s by slicing off all leading and
|
2011-01-09 12:11:16 -05:00
|
|
|
// trailing white space, as defined by Unicode.
|
2010-05-18 23:01:05 -07:00
|
|
|
func TrimSpace(s []byte) []byte {
|
|
|
|
|
return TrimFunc(s, unicode.IsSpace)
|
2009-09-01 13:46:59 -07:00
|
|
|
}
|
2009-09-13 21:35:18 -07:00
|
|
|
|
2009-12-02 20:47:38 -08:00
|
|
|
// Runes returns a slice of runes (Unicode code points) equivalent to s.
|
2011-10-25 22:22:09 -07:00
|
|
|
func Runes(s []byte) []rune {
|
|
|
|
|
t := make([]rune, utf8.RuneCount(s))
|
2009-12-15 15:33:31 -08:00
|
|
|
i := 0
|
2009-12-02 20:47:38 -08:00
|
|
|
for len(s) > 0 {
|
2009-12-15 15:33:31 -08:00
|
|
|
r, l := utf8.DecodeRune(s)
|
|
|
|
|
t[i] = r
|
|
|
|
|
i++
|
|
|
|
|
s = s[l:]
|
2009-12-02 20:47:38 -08:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return t
|
2009-12-02 20:47:38 -08:00
|
|
|
}
|
2010-06-30 18:03:09 -07:00
|
|
|
|
|
|
|
|
// Replace returns a copy of the slice s with the first n
|
|
|
|
|
// non-overlapping instances of old replaced by new.
|
2014-08-25 14:42:27 -07:00
|
|
|
// If old is empty, it matches at the beginning of the slice
|
|
|
|
|
// and after each UTF-8 sequence, yielding up to k+1 replacements
|
|
|
|
|
// for a k-rune slice.
|
2010-07-01 14:08:14 -07:00
|
|
|
// If n < 0, there is no limit on the number of replacements.
|
2010-06-30 18:03:09 -07:00
|
|
|
func Replace(s, old, new []byte, n int) []byte {
|
2011-09-21 12:36:17 -03:00
|
|
|
m := 0
|
|
|
|
|
if n != 0 {
|
|
|
|
|
// Compute number of replacements.
|
|
|
|
|
m = Count(s, old)
|
|
|
|
|
}
|
|
|
|
|
if m == 0 {
|
2012-07-20 16:04:22 -03:00
|
|
|
// Just return a copy.
|
|
|
|
|
return append([]byte(nil), s...)
|
2011-09-21 12:36:17 -03:00
|
|
|
}
|
|
|
|
|
if n < 0 || m < n {
|
2010-06-30 18:03:09 -07:00
|
|
|
n = m
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply replacements to buffer.
|
|
|
|
|
t := make([]byte, len(s)+n*(len(new)-len(old)))
|
|
|
|
|
w := 0
|
|
|
|
|
start := 0
|
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
|
j := start
|
|
|
|
|
if len(old) == 0 {
|
|
|
|
|
if i > 0 {
|
|
|
|
|
_, wid := utf8.DecodeRune(s[start:])
|
|
|
|
|
j += wid
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
j += Index(s[start:], old)
|
|
|
|
|
}
|
|
|
|
|
w += copy(t[w:], s[start:j])
|
|
|
|
|
w += copy(t[w:], new)
|
|
|
|
|
start = j + len(old)
|
|
|
|
|
}
|
|
|
|
|
w += copy(t[w:], s[start:])
|
|
|
|
|
return t[0:w]
|
|
|
|
|
}
|
2011-09-26 19:35:32 -04:00
|
|
|
|
|
|
|
|
// EqualFold reports whether s and t, interpreted as UTF-8 strings,
|
|
|
|
|
// are equal under Unicode case-folding.
|
|
|
|
|
func EqualFold(s, t []byte) bool {
|
|
|
|
|
for len(s) != 0 && len(t) != 0 {
|
|
|
|
|
// Extract first rune from each.
|
2011-10-25 22:22:09 -07:00
|
|
|
var sr, tr rune
|
2011-09-26 19:35:32 -04:00
|
|
|
if s[0] < utf8.RuneSelf {
|
2011-10-25 22:22:09 -07:00
|
|
|
sr, s = rune(s[0]), s[1:]
|
2011-09-26 19:35:32 -04:00
|
|
|
} else {
|
|
|
|
|
r, size := utf8.DecodeRune(s)
|
|
|
|
|
sr, s = r, s[size:]
|
|
|
|
|
}
|
|
|
|
|
if t[0] < utf8.RuneSelf {
|
2011-10-25 22:22:09 -07:00
|
|
|
tr, t = rune(t[0]), t[1:]
|
2011-09-26 19:35:32 -04:00
|
|
|
} else {
|
|
|
|
|
r, size := utf8.DecodeRune(t)
|
|
|
|
|
tr, t = r, t[size:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If they match, keep going; if not, return false.
|
|
|
|
|
|
|
|
|
|
// Easy case.
|
|
|
|
|
if tr == sr {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sr < tr to simplify what follows.
|
|
|
|
|
if tr < sr {
|
|
|
|
|
tr, sr = sr, tr
|
|
|
|
|
}
|
|
|
|
|
// Fast check for ASCII.
|
|
|
|
|
if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' {
|
|
|
|
|
// ASCII, and sr is upper case. tr must be lower case.
|
|
|
|
|
if tr == sr+'a'-'A' {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// General case. SimpleFold(x) returns the next equivalent rune > x
|
2011-09-26 19:35:32 -04:00
|
|
|
// or wraps around to smaller values.
|
|
|
|
|
r := unicode.SimpleFold(sr)
|
|
|
|
|
for r != sr && r < tr {
|
|
|
|
|
r = unicode.SimpleFold(r)
|
|
|
|
|
}
|
|
|
|
|
if r == tr {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// One string is empty. Are both?
|
2011-09-26 19:35:32 -04:00
|
|
|
return len(s) == len(t)
|
|
|
|
|
}
|