2008-09-12 16:12:20 -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.
|
|
|
|
|
|
2015-06-03 12:21:06 -07:00
|
|
|
// Package strings implements simple functions to manipulate UTF-8 encoded strings.
|
|
|
|
|
//
|
2015-07-11 08:51:20 -06:00
|
|
|
// For information about UTF-8 strings in Go, see https://blog.golang.org/strings.
|
2008-09-12 16:12:20 -07:00
|
|
|
package strings
|
|
|
|
|
|
2009-09-01 11:06:28 -07:00
|
|
|
import (
|
2018-03-03 10:28:58 -08:00
|
|
|
"internal/bytealg"
|
2009-12-15 15:40:16 -08:00
|
|
|
"unicode"
|
2011-11-08 15:41:54 -08:00
|
|
|
"unicode/utf8"
|
2009-09-01 11:06:28 -07:00
|
|
|
)
|
2008-09-12 16:12:20 -07:00
|
|
|
|
2016-03-26 00:04:48 +01:00
|
|
|
// explode splits s into a slice of UTF-8 strings,
|
|
|
|
|
// one string per Unicode character up to a maximum of n (n < 0 means no limit).
|
|
|
|
|
// Invalid UTF-8 sequences become correct encodings of U+FFFD.
|
2009-06-24 19:02:29 -07:00
|
|
|
func explode(s string, n int) []string {
|
2010-03-04 16:15:51 -08:00
|
|
|
l := utf8.RuneCountInString(s)
|
2016-03-26 00:04:48 +01:00
|
|
|
if n < 0 || n > l {
|
2010-03-04 16:15:51 -08:00
|
|
|
n = l
|
2009-06-24 19:02:29 -07:00
|
|
|
}
|
2009-12-15 15:40:16 -08:00
|
|
|
a := make([]string, n)
|
2016-03-26 00:04:48 +01:00
|
|
|
for i := 0; i < n-1; i++ {
|
|
|
|
|
ch, size := utf8.DecodeRuneInString(s)
|
|
|
|
|
a[i] = s[:size]
|
|
|
|
|
s = s[size:]
|
2013-03-06 15:21:19 -05:00
|
|
|
if ch == utf8.RuneError {
|
|
|
|
|
a[i] = string(utf8.RuneError)
|
|
|
|
|
}
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2016-03-26 00:04:48 +01:00
|
|
|
if n > 0 {
|
|
|
|
|
a[n-1] = s
|
2010-08-03 13:35:14 +10:00
|
|
|
}
|
2010-03-04 16:15:51 -08:00
|
|
|
return a
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
|
|
|
|
|
2013-02-17 13:07:17 +01:00
|
|
|
// primeRK is the prime base used in Rabin-Karp algorithm.
|
|
|
|
|
const primeRK = 16777619
|
|
|
|
|
|
2014-09-01 17:47:57 +10:00
|
|
|
// hashStr returns the hash and the appropriate multiplicative
|
2013-02-17 13:07:17 +01:00
|
|
|
// factor for use in Rabin-Karp algorithm.
|
2014-09-01 17:47:57 +10:00
|
|
|
func hashStr(sep string) (uint32, uint32) {
|
2013-02-17 13:07:17 +01:00
|
|
|
hash := uint32(0)
|
|
|
|
|
for i := 0; i < len(sep); i++ {
|
|
|
|
|
hash = hash*primeRK + uint32(sep[i])
|
2014-09-01 17:47:57 +10:00
|
|
|
}
|
|
|
|
|
var pow, sq uint32 = 1, primeRK
|
|
|
|
|
for i := len(sep); i > 0; i >>= 1 {
|
|
|
|
|
if i&1 != 0 {
|
|
|
|
|
pow *= sq
|
|
|
|
|
}
|
|
|
|
|
sq *= sq
|
|
|
|
|
}
|
|
|
|
|
return hash, pow
|
|
|
|
|
}
|
2013-02-17 13:07:17 +01:00
|
|
|
|
2014-09-01 17:47:57 +10:00
|
|
|
// hashStrRev returns the hash of the reverse of sep and the
|
|
|
|
|
// appropriate multiplicative factor for use in Rabin-Karp algorithm.
|
|
|
|
|
func hashStrRev(sep string) (uint32, uint32) {
|
|
|
|
|
hash := uint32(0)
|
|
|
|
|
for i := len(sep) - 1; i >= 0; i-- {
|
|
|
|
|
hash = hash*primeRK + uint32(sep[i])
|
2013-02-17 13:07:17 +01:00
|
|
|
}
|
|
|
|
|
var pow, sq uint32 = 1, primeRK
|
|
|
|
|
for i := len(sep); i > 0; i >>= 1 {
|
|
|
|
|
if i&1 != 0 {
|
|
|
|
|
pow *= sq
|
|
|
|
|
}
|
|
|
|
|
sq *= sq
|
|
|
|
|
}
|
|
|
|
|
return hash, pow
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-03 10:28:58 -08:00
|
|
|
// Count counts the number of non-overlapping instances of substr in s.
|
|
|
|
|
// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
|
|
|
|
|
func Count(s, substr string) int {
|
2017-02-07 16:00:39 -06:00
|
|
|
// special case
|
2017-02-11 20:06:54 +01:00
|
|
|
if len(substr) == 0 {
|
2013-02-19 10:36:15 -05:00
|
|
|
return utf8.RuneCountInString(s) + 1
|
|
|
|
|
}
|
2018-03-03 10:28:58 -08:00
|
|
|
if len(substr) == 1 {
|
|
|
|
|
return bytealg.CountString(s, substr[0])
|
|
|
|
|
}
|
2017-04-03 16:08:13 -07:00
|
|
|
n := 0
|
2016-09-07 16:35:32 +03:00
|
|
|
for {
|
2017-02-11 20:06:54 +01:00
|
|
|
i := Index(s, substr)
|
2016-09-07 16:35:32 +03:00
|
|
|
if i == -1 {
|
|
|
|
|
return n
|
2013-02-17 13:07:17 +01:00
|
|
|
}
|
2016-09-07 16:35:32 +03:00
|
|
|
n++
|
2017-02-11 20:06:54 +01:00
|
|
|
s = s[i+len(substr):]
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 15:44:42 -08:00
|
|
|
// Contains reports whether substr is within s.
|
2010-11-01 14:32:48 -07:00
|
|
|
func Contains(s, substr string) bool {
|
2011-11-23 20:20:14 -08:00
|
|
|
return Index(s, substr) >= 0
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 15:44:42 -08:00
|
|
|
// ContainsAny reports whether any Unicode code points in chars are within s.
|
2011-11-23 20:20:14 -08:00
|
|
|
func ContainsAny(s, chars string) bool {
|
|
|
|
|
return IndexAny(s, chars) >= 0
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-17 15:44:42 -08:00
|
|
|
// ContainsRune reports whether the Unicode code point r is within s.
|
2011-11-23 20:20:14 -08:00
|
|
|
func ContainsRune(s string, r rune) bool {
|
|
|
|
|
return IndexRune(s, r) >= 0
|
2010-11-01 14:32:48 -07:00
|
|
|
}
|
|
|
|
|
|
2017-02-11 20:06:54 +01:00
|
|
|
// LastIndex returns the index of the last instance of substr in s, or -1 if substr is not present in s.
|
|
|
|
|
func LastIndex(s, substr string) int {
|
|
|
|
|
n := len(substr)
|
2014-09-01 17:47:57 +10:00
|
|
|
switch {
|
|
|
|
|
case n == 0:
|
2009-11-09 12:07:39 -08:00
|
|
|
return len(s)
|
2014-09-01 17:47:57 +10:00
|
|
|
case n == 1:
|
2017-02-11 20:06:54 +01:00
|
|
|
return LastIndexByte(s, substr[0])
|
2014-09-01 17:47:57 +10:00
|
|
|
case n == len(s):
|
2017-02-11 20:06:54 +01:00
|
|
|
if substr == s {
|
2014-09-01 17:47:57 +10:00
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
case n > len(s):
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
// Rabin-Karp search from the end of the string
|
2017-02-11 20:06:54 +01:00
|
|
|
hashss, pow := hashStrRev(substr)
|
2014-09-01 17:47:57 +10:00
|
|
|
last := len(s) - n
|
|
|
|
|
var h uint32
|
|
|
|
|
for i := len(s) - 1; i >= last; i-- {
|
|
|
|
|
h = h*primeRK + uint32(s[i])
|
2009-11-18 19:23:08 -08:00
|
|
|
}
|
2017-02-11 20:06:54 +01:00
|
|
|
if h == hashss && s[last:] == substr {
|
2014-09-01 17:47:57 +10:00
|
|
|
return last
|
|
|
|
|
}
|
|
|
|
|
for i := last - 1; i >= 0; i-- {
|
|
|
|
|
h *= primeRK
|
|
|
|
|
h += uint32(s[i])
|
|
|
|
|
h -= pow * uint32(s[i+n])
|
2017-02-11 20:06:54 +01:00
|
|
|
if h == hashss && s[i:i+n] == substr {
|
2009-11-09 12:07:39 -08:00
|
|
|
return i
|
2009-06-09 10:58:58 -07:00
|
|
|
}
|
|
|
|
|
}
|
2009-12-15 15:40:16 -08:00
|
|
|
return -1
|
2009-06-09 10:58:58 -07:00
|
|
|
}
|
2008-09-12 16:12:20 -07:00
|
|
|
|
2010-04-09 18:57:03 -07:00
|
|
|
// IndexRune returns the index of the first instance of the Unicode code point
|
2011-10-25 22:22:09 -07:00
|
|
|
// r, or -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 string, r rune) int {
|
2016-10-26 14:18:37 -07:00
|
|
|
switch {
|
|
|
|
|
case 0 <= r && r < utf8.RuneSelf:
|
2014-10-06 15:10:51 -07:00
|
|
|
return IndexByte(s, byte(r))
|
2016-10-26 14:18:37 -07:00
|
|
|
case r == utf8.RuneError:
|
|
|
|
|
for i, r := range s {
|
|
|
|
|
if r == utf8.RuneError {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
case !utf8.ValidRune(r):
|
|
|
|
|
return -1
|
|
|
|
|
default:
|
|
|
|
|
return Index(s, string(r))
|
2010-04-09 18:57:03 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-26 13:05:04 -07:00
|
|
|
// IndexAny returns the index of the first instance of any Unicode code point
|
|
|
|
|
// from chars in s, or -1 if no Unicode code point from chars is present in s.
|
|
|
|
|
func IndexAny(s, chars string) int {
|
bytes, strings: restore O(1) behavior of IndexAny(s, "") and LastIndexAny(s, "")
CL 65851 (bytes) and CL 65910 (strings) “improve[d] readability”
by removing the special case that bypassed the whole function body
when chars == "". In doing so, yes, the function was unindented a
level, which is nice, but the runtime of that case went from O(1) to O(n)
where n = len(s).
I don't know if anyone's code depends on the O(1) behavior in this case,
but quite possibly someone's does.
This CL adds the special case back, with a comment to prevent future
deletions, and without reindenting each function body in full.
Change-Id: I5aba33922b304dd1b8657e6d51d6c937a7f95c81
Reviewed-on: https://go-review.googlesource.com/78112
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2017-11-13 21:30:22 -05:00
|
|
|
if chars == "" {
|
|
|
|
|
// Avoid scanning all of s.
|
|
|
|
|
return -1
|
|
|
|
|
}
|
2017-09-25 14:54:37 +02:00
|
|
|
if len(s) > 8 {
|
|
|
|
|
if as, isASCII := makeASCIISet(chars); isASCII {
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
if as.contains(s[i]) {
|
|
|
|
|
return i
|
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
|
|
|
}
|
|
|
|
|
}
|
2017-09-25 14:54:37 +02:00
|
|
|
return -1
|
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
|
|
|
}
|
2017-09-25 14:54:37 +02:00
|
|
|
}
|
|
|
|
|
for i, c := range s {
|
|
|
|
|
for _, m := range chars {
|
|
|
|
|
if c == m {
|
|
|
|
|
return i
|
2010-03-26 13:05:04 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
2010-11-12 12:47:50 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LastIndexAny returns the index of the last instance of any Unicode code
|
|
|
|
|
// point from chars in s, or -1 if no Unicode code point from chars is
|
|
|
|
|
// present in s.
|
|
|
|
|
func LastIndexAny(s, chars string) int {
|
bytes, strings: restore O(1) behavior of IndexAny(s, "") and LastIndexAny(s, "")
CL 65851 (bytes) and CL 65910 (strings) “improve[d] readability”
by removing the special case that bypassed the whole function body
when chars == "". In doing so, yes, the function was unindented a
level, which is nice, but the runtime of that case went from O(1) to O(n)
where n = len(s).
I don't know if anyone's code depends on the O(1) behavior in this case,
but quite possibly someone's does.
This CL adds the special case back, with a comment to prevent future
deletions, and without reindenting each function body in full.
Change-Id: I5aba33922b304dd1b8657e6d51d6c937a7f95c81
Reviewed-on: https://go-review.googlesource.com/78112
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2017-11-13 21:30:22 -05:00
|
|
|
if chars == "" {
|
|
|
|
|
// Avoid scanning all of s.
|
|
|
|
|
return -1
|
|
|
|
|
}
|
2017-09-25 14:54:37 +02: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
|
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
|
|
|
}
|
|
|
|
|
}
|
2017-09-25 14:54:37 +02:00
|
|
|
return -1
|
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
|
|
|
}
|
2017-09-25 14:54:37 +02:00
|
|
|
}
|
|
|
|
|
for i := len(s); i > 0; {
|
|
|
|
|
r, size := utf8.DecodeLastRuneInString(s[:i])
|
|
|
|
|
i -= size
|
|
|
|
|
for _, c := range chars {
|
|
|
|
|
if r == c {
|
|
|
|
|
return i
|
2010-11-12 12:47:50 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
2010-03-26 13:05:04 -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 string, c byte) int {
|
|
|
|
|
for i := len(s) - 1; i >= 0; i-- {
|
|
|
|
|
if s[i] == c {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-04 15:19:30 -08:00
|
|
|
// Generic split: splits after each instance of sep,
|
|
|
|
|
// including sepSave bytes of sep in the subarrays.
|
|
|
|
|
func genSplit(s, sep string, sepSave, n int) []string {
|
2010-07-01 14:08:14 -07:00
|
|
|
if n == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2008-09-12 16:12:20 -07:00
|
|
|
if sep == "" {
|
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
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2017-02-07 13:38:52 +02:00
|
|
|
|
2009-12-15 15:40:16 -08:00
|
|
|
a := make([]string, n)
|
2017-02-07 13:38:52 +02:00
|
|
|
n--
|
|
|
|
|
i := 0
|
|
|
|
|
for i < n {
|
|
|
|
|
m := Index(s, sep)
|
|
|
|
|
if m < 0 {
|
|
|
|
|
break
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2017-02-07 13:38:52 +02:00
|
|
|
a[i] = s[:m+sepSave]
|
|
|
|
|
s = s[m+len(sep):]
|
|
|
|
|
i++
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2017-02-07 13:38:52 +02:00
|
|
|
a[i] = s
|
|
|
|
|
return a[:i+1]
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2008-12-04 21:00:34 -08:00
|
|
|
|
2011-06-28 09:43:14 +10:00
|
|
|
// SplitN slices s into substrings separated by sep and returns a slice of
|
2010-07-27 15:06:08 +10:00
|
|
|
// the substrings between those separators.
|
2017-06-06 11:25:17 +02:00
|
|
|
//
|
2010-07-27 15:06:08 +10:00
|
|
|
// The count determines the number of substrings to return:
|
|
|
|
|
// n > 0: at most n substrings; the last substring will be the unsplit remainder.
|
|
|
|
|
// n == 0: the result is nil (zero substrings)
|
|
|
|
|
// n < 0: all substrings
|
2017-06-06 11:25:17 +02:00
|
|
|
//
|
|
|
|
|
// Edge cases for s and sep (for example, empty strings) are handled
|
|
|
|
|
// as described in the documentation for Split.
|
2011-06-28 09:43:14 +10:00
|
|
|
func SplitN(s, sep string, n int) []string { 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 substrings after each instance of sep and
|
2010-07-27 15:06:08 +10:00
|
|
|
// returns a slice of those substrings.
|
2017-06-06 11:25:17 +02:00
|
|
|
//
|
2010-07-27 15:06:08 +10:00
|
|
|
// The count determines the number of substrings to return:
|
|
|
|
|
// n > 0: at most n substrings; the last substring will be the unsplit remainder.
|
|
|
|
|
// n == 0: the result is nil (zero substrings)
|
|
|
|
|
// n < 0: all substrings
|
2017-06-06 11:25:17 +02:00
|
|
|
//
|
|
|
|
|
// Edge cases for s and sep (for example, empty strings) are handled
|
|
|
|
|
// as described in the documentation for SplitAfter.
|
2011-06-28 09:43:14 +10:00
|
|
|
func SplitAfterN(s, sep string, n int) []string {
|
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 substrings separated by sep and returns a slice of
|
|
|
|
|
// the substrings between those separators.
|
2017-06-06 11:25:17 +02:00
|
|
|
//
|
|
|
|
|
// If s does not contain sep and sep is not empty, Split returns a
|
|
|
|
|
// slice of length 1 whose only element is s.
|
|
|
|
|
//
|
|
|
|
|
// If sep is empty, Split splits after each UTF-8 sequence. If both s
|
|
|
|
|
// and sep are empty, Split returns an empty slice.
|
|
|
|
|
//
|
2011-06-28 09:43:14 +10:00
|
|
|
// It is equivalent to SplitN with a count of -1.
|
|
|
|
|
func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) }
|
|
|
|
|
|
|
|
|
|
// SplitAfter slices s into all substrings after each instance of sep and
|
|
|
|
|
// returns a slice of those substrings.
|
2017-06-06 11:25:17 +02:00
|
|
|
//
|
|
|
|
|
// If s does not contain sep and sep is not empty, SplitAfter returns
|
|
|
|
|
// a slice of length 1 whose only element is s.
|
|
|
|
|
//
|
|
|
|
|
// If sep is empty, SplitAfter splits after each UTF-8 sequence. If
|
|
|
|
|
// both s and sep are empty, SplitAfter returns an empty slice.
|
|
|
|
|
//
|
2011-06-28 09:43:14 +10:00
|
|
|
// It is equivalent to SplitAfterN with a count of -1.
|
|
|
|
|
func SplitAfter(s, sep string) []string {
|
|
|
|
|
return genSplit(s, sep, len(sep), -1)
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-06 09:34:39 +01:00
|
|
|
var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
|
|
|
|
|
|
2009-12-15 21:09:55 -08:00
|
|
|
// Fields splits the string s around each instance of one or more consecutive white space
|
2017-08-14 21:12:49 +02:00
|
|
|
// characters, as defined by unicode.IsSpace, returning a slice of substrings of s or an
|
|
|
|
|
// empty slice if s contains only white space.
|
2009-12-15 21:09:55 -08:00
|
|
|
func Fields(s string) []string {
|
2017-03-06 09:34:39 +01: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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if setBits < utf8.RuneSelf { // ASCII fast path
|
|
|
|
|
a := make([]string, 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 {
|
|
|
|
|
i++
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
a[na] = s[fieldStart:i]
|
|
|
|
|
na++
|
|
|
|
|
i++
|
|
|
|
|
// Skip spaces in between fields.
|
|
|
|
|
for i < len(s) && asciiSpace[s[i]] != 0 {
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
fieldStart = i
|
|
|
|
|
}
|
|
|
|
|
if fieldStart < len(s) { // Last field might end at EOF.
|
|
|
|
|
a[na] = s[fieldStart:]
|
|
|
|
|
}
|
|
|
|
|
return a
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Some runes in the input string are not ASCII.
|
2017-05-02 10:09:18 +02:00
|
|
|
return FieldsFunc(s, unicode.IsSpace)
|
2010-04-19 16:36:50 -07:00
|
|
|
}
|
|
|
|
|
|
2010-08-05 23:11:06 +10:00
|
|
|
// FieldsFunc splits the string s at each run of Unicode code points c satisfying f(c)
|
2011-01-04 16:23:29 -08:00
|
|
|
// and returns an array of slices of s. If all code points in s satisfy f(c) or the
|
|
|
|
|
// string is empty, 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 string, f func(rune) bool) []string {
|
2017-05-02 10:09:18 +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-05-02 10:09:18 +02:00
|
|
|
spans := make([]span, 0, 32)
|
2009-12-15 21:09:55 -08:00
|
|
|
|
2017-05-02 10:09:18 +02:00
|
|
|
// Find the field start and end indices.
|
|
|
|
|
wasField := false
|
|
|
|
|
fromIndex := 0
|
2009-12-15 21:09:55 -08:00
|
|
|
for i, rune := range s {
|
2010-04-19 16:36:50 -07:00
|
|
|
if f(rune) {
|
2017-05-02 10:09:18 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-02 10:09:18 +02:00
|
|
|
|
|
|
|
|
// Last field might end at EOF.
|
|
|
|
|
if wasField {
|
|
|
|
|
spans = append(spans, span{fromIndex, len(s)})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create strings from recorded field indices.
|
|
|
|
|
a := make([]string, len(spans))
|
|
|
|
|
for i, span := range spans {
|
|
|
|
|
a[i] = s[span.start:span.end]
|
2009-12-15 21:09:55 -08:00
|
|
|
}
|
2017-05-02 10:09:18 +02:00
|
|
|
|
2010-03-29 13:39:16 -07:00
|
|
|
return a
|
2009-12-15 21:09:55 -08:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// Join concatenates the elements of a to create a single string. The separator string
|
2009-03-05 17:02:34 -08:00
|
|
|
// sep is placed between elements in the resulting string.
|
2009-01-20 14:40:40 -08:00
|
|
|
func Join(a []string, sep string) string {
|
2016-07-18 17:27:21 +00:00
|
|
|
switch len(a) {
|
|
|
|
|
case 0:
|
2009-11-09 12:07:39 -08:00
|
|
|
return ""
|
2016-07-18 17:27:21 +00:00
|
|
|
case 1:
|
2009-11-09 12:07:39 -08:00
|
|
|
return a[0]
|
2016-07-18 17:27:21 +00:00
|
|
|
case 2:
|
|
|
|
|
// Special case for common small values.
|
|
|
|
|
// Remove if golang.org/issue/6714 is fixed
|
|
|
|
|
return a[0] + sep + a[1]
|
|
|
|
|
case 3:
|
|
|
|
|
// Special case for common small values.
|
|
|
|
|
// Remove if golang.org/issue/6714 is fixed
|
|
|
|
|
return a[0] + sep + a[1] + sep + a[2]
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2009-12-15 15:40:16 -08:00
|
|
|
n := len(sep) * (len(a) - 1)
|
2008-09-12 16:12:20 -07:00
|
|
|
for i := 0; i < len(a); i++ {
|
2009-11-09 12:07:39 -08:00
|
|
|
n += len(a[i])
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
|
|
|
|
|
2009-12-15 15:40:16 -08:00
|
|
|
b := make([]byte, n)
|
2011-03-29 01:27:38 -04:00
|
|
|
bp := copy(b, a[0])
|
|
|
|
|
for _, s := range a[1:] {
|
|
|
|
|
bp += copy(b[bp:], sep)
|
|
|
|
|
bp += copy(b[bp:], s)
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2009-12-15 15:40:16 -08:00
|
|
|
return string(b)
|
2008-09-12 16:12:20 -07:00
|
|
|
}
|
2009-04-13 16:50:42 -07:00
|
|
|
|
|
|
|
|
// HasPrefix tests whether the string s begins with prefix.
|
|
|
|
|
func HasPrefix(s, prefix string) bool {
|
2009-11-09 12:07:39 -08:00
|
|
|
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
|
2009-04-13 16:50:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HasSuffix tests whether the string s ends with suffix.
|
|
|
|
|
func HasSuffix(s, suffix string) bool {
|
2009-11-20 11:45:05 -08:00
|
|
|
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
|
2009-04-13 16:50:42 -07:00
|
|
|
}
|
2009-06-05 13:09:03 -07:00
|
|
|
|
2009-09-01 11:06:28 -07:00
|
|
|
// Map returns a copy of the string 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
|
|
|
|
|
// dropped from the string with no replacement.
|
2011-10-25 22:22:09 -07:00
|
|
|
func Map(mapping func(rune) rune, s string) string {
|
2009-09-01 11:06:28 -07:00
|
|
|
// In the worst case, the string 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.
|
2016-11-14 08:05:45 +01:00
|
|
|
|
2011-03-28 09:41:57 -07:00
|
|
|
// The output buffer b is initialized on demand, the first
|
|
|
|
|
// time a character differs.
|
|
|
|
|
var b []byte
|
2016-11-14 08:05:45 +01:00
|
|
|
// nbytes is the number of bytes encoded in b.
|
|
|
|
|
var nbytes int
|
2011-03-28 09:41:57 -07:00
|
|
|
|
|
|
|
|
for i, c := range s {
|
2011-10-25 22:22:09 -07:00
|
|
|
r := mapping(c)
|
2016-11-14 08:05:45 +01:00
|
|
|
if r == c {
|
|
|
|
|
continue
|
2011-03-28 09:41:57 -07:00
|
|
|
}
|
2016-11-14 08:05:45 +01:00
|
|
|
|
|
|
|
|
b = make([]byte, len(s)+utf8.UTFMax)
|
|
|
|
|
nbytes = copy(b, s[:i])
|
2011-10-25 22:22:09 -07:00
|
|
|
if r >= 0 {
|
2018-05-04 06:54:18 +02:00
|
|
|
if r < utf8.RuneSelf {
|
2016-11-14 08:05:45 +01:00
|
|
|
b[nbytes] = byte(r)
|
|
|
|
|
nbytes++
|
|
|
|
|
} else {
|
|
|
|
|
nbytes += utf8.EncodeRune(b[nbytes:], r)
|
2009-09-01 11:06:28 -07:00
|
|
|
}
|
|
|
|
|
}
|
2017-02-28 21:21:45 +01:00
|
|
|
|
|
|
|
|
if c == utf8.RuneError {
|
|
|
|
|
// RuneError is the result of either decoding
|
|
|
|
|
// an invalid sequence or '\uFFFD'. Determine
|
|
|
|
|
// the correct number of bytes we need to advance.
|
|
|
|
|
_, w := utf8.DecodeRuneInString(s[i:])
|
|
|
|
|
i += w
|
|
|
|
|
} else {
|
|
|
|
|
i += utf8.RuneLen(c)
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-14 08:05:45 +01:00
|
|
|
s = s[i:]
|
|
|
|
|
break
|
2009-06-05 13:09:03 -07:00
|
|
|
}
|
2016-11-14 08:05:45 +01:00
|
|
|
|
2011-03-28 09:41:57 -07:00
|
|
|
if b == nil {
|
|
|
|
|
return s
|
|
|
|
|
}
|
2016-11-14 08:05:45 +01:00
|
|
|
|
|
|
|
|
for _, c := range s {
|
|
|
|
|
r := mapping(c)
|
|
|
|
|
|
|
|
|
|
// common case
|
2018-05-04 06:54:18 +02:00
|
|
|
if (0 <= r && r < utf8.RuneSelf) && nbytes < len(b) {
|
2016-11-14 08:05:45 +01:00
|
|
|
b[nbytes] = byte(r)
|
|
|
|
|
nbytes++
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// b is not big enough or r is not a ASCII rune.
|
|
|
|
|
if r >= 0 {
|
|
|
|
|
if nbytes+utf8.UTFMax >= len(b) {
|
|
|
|
|
// Grow the buffer.
|
|
|
|
|
nb := make([]byte, 2*len(b))
|
|
|
|
|
copy(nb, b[:nbytes])
|
|
|
|
|
b = nb
|
|
|
|
|
}
|
|
|
|
|
nbytes += utf8.EncodeRune(b[nbytes:], r)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(b[:nbytes])
|
2009-06-05 13:09:03 -07:00
|
|
|
}
|
|
|
|
|
|
2009-11-16 12:40:01 -08:00
|
|
|
// Repeat returns a new string consisting of count copies of the string s.
|
2016-09-28 01:54:38 -07:00
|
|
|
//
|
|
|
|
|
// It panics if count is negative or if
|
|
|
|
|
// the result of (len(s) * count) overflows.
|
2009-11-16 12:40:01 -08:00
|
|
|
func Repeat(s string, count int) string {
|
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("strings: negative Repeat count")
|
|
|
|
|
} else if count > 0 && len(s)*count/count != len(s) {
|
|
|
|
|
panic("strings: Repeat count causes overflow")
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-15 15:40:16 -08:00
|
|
|
b := make([]byte, len(s)*count)
|
2014-06-11 19:03:59 -07:00
|
|
|
bp := copy(b, s)
|
|
|
|
|
for bp < len(b) {
|
|
|
|
|
copy(b[bp:], b[:bp])
|
|
|
|
|
bp *= 2
|
2009-11-16 12:40:01 -08:00
|
|
|
}
|
2009-12-15 15:40:16 -08:00
|
|
|
return string(b)
|
2009-11-16 12:40:01 -08:00
|
|
|
}
|
|
|
|
|
|
2009-09-01 13:46:59 -07:00
|
|
|
// ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case.
|
2017-09-28 15:07:48 +05:30
|
|
|
func ToUpper(s string) string {
|
|
|
|
|
isASCII, hasLower := true, false
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
c := s[i]
|
|
|
|
|
if c >= utf8.RuneSelf {
|
|
|
|
|
isASCII = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
hasLower = hasLower || (c >= 'a' && c <= 'z')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isASCII { // optimize for ASCII-only strings.
|
|
|
|
|
if !hasLower {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
b := make([]byte, len(s))
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
c := s[i]
|
|
|
|
|
if c >= 'a' && c <= 'z' {
|
|
|
|
|
c -= 'a' - 'A'
|
|
|
|
|
}
|
|
|
|
|
b[i] = c
|
|
|
|
|
}
|
|
|
|
|
return string(b)
|
|
|
|
|
}
|
|
|
|
|
return Map(unicode.ToUpper, s)
|
|
|
|
|
}
|
2009-06-05 13:09:03 -07:00
|
|
|
|
2009-11-30 12:50:02 -08:00
|
|
|
// ToLower returns a copy of the string s with all Unicode letters mapped to their lower case.
|
2017-11-08 09:15:53 +05:30
|
|
|
func ToLower(s string) string {
|
|
|
|
|
isASCII, hasUpper := true, false
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
c := s[i]
|
|
|
|
|
if c >= utf8.RuneSelf {
|
|
|
|
|
isASCII = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
hasUpper = hasUpper || (c >= 'A' && c <= 'Z')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isASCII { // optimize for ASCII-only strings.
|
|
|
|
|
if !hasUpper {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
b := make([]byte, len(s))
|
|
|
|
|
for i := 0; i < len(s); i++ {
|
|
|
|
|
c := s[i]
|
|
|
|
|
if c >= 'A' && c <= 'Z' {
|
|
|
|
|
c += 'a' - 'A'
|
|
|
|
|
}
|
|
|
|
|
b[i] = c
|
|
|
|
|
}
|
|
|
|
|
return string(b)
|
|
|
|
|
}
|
|
|
|
|
return Map(unicode.ToLower, s)
|
|
|
|
|
}
|
2009-06-05 13:09:03 -07:00
|
|
|
|
2009-09-01 13:46:59 -07:00
|
|
|
// ToTitle returns a copy of the string s with all Unicode letters mapped to their title case.
|
2009-12-15 15:40:16 -08:00
|
|
|
func ToTitle(s string) string { return Map(unicode.ToTitle, s) }
|
2009-09-01 11:06:28 -07:00
|
|
|
|
2010-03-30 17:51:03 -07:00
|
|
|
// ToUpperSpecial returns a copy of the string s with all Unicode letters mapped to their
|
2018-08-03 18:08:09 +00:00
|
|
|
// upper case using the case mapping specified by c.
|
2016-10-26 13:20:05 -07:00
|
|
|
func ToUpperSpecial(c unicode.SpecialCase, s string) string {
|
|
|
|
|
return Map(func(r rune) rune { return c.ToUpper(r) }, s)
|
2010-03-30 17:51:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToLowerSpecial returns a copy of the string s with all Unicode letters mapped to their
|
2018-08-03 18:08:09 +00:00
|
|
|
// lower case using the case mapping specified by c.
|
2016-10-26 13:20:05 -07:00
|
|
|
func ToLowerSpecial(c unicode.SpecialCase, s string) string {
|
|
|
|
|
return Map(func(r rune) rune { return c.ToLower(r) }, s)
|
2010-03-30 17:51:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToTitleSpecial returns a copy of the string s with all Unicode letters mapped to their
|
|
|
|
|
// title case, giving priority to the special casing rules.
|
2016-10-26 13:20:05 -07:00
|
|
|
func ToTitleSpecial(c unicode.SpecialCase, s string) string {
|
|
|
|
|
return Map(func(r rune) rune { return c.ToTitle(r) }, s)
|
2010-03-30 17:51:03 -07:00
|
|
|
}
|
|
|
|
|
|
2010-07-20 00:03: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 00:03:59 -07:00
|
|
|
// ASCII alphanumerics and underscore are not separators
|
2011-10-25 22:22:09 -07:00
|
|
|
if r <= 0x7F {
|
2010-07-20 00:03:59 -07:00
|
|
|
switch {
|
2011-10-25 22:22:09 -07:00
|
|
|
case '0' <= r && r <= '9':
|
2010-07-20 00:03:59 -07:00
|
|
|
return false
|
2011-10-25 22:22:09 -07:00
|
|
|
case 'a' <= r && r <= 'z':
|
2010-07-20 00:03:59 -07:00
|
|
|
return false
|
2011-10-25 22:22:09 -07:00
|
|
|
case 'A' <= r && r <= 'Z':
|
2010-07-20 00:03:59 -07:00
|
|
|
return false
|
2011-10-25 22:22:09 -07:00
|
|
|
case r == '_':
|
2010-07-20 00:03: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 00:03: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 00:03:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Title returns a copy of the string 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 00:03:59 -07:00
|
|
|
func Title(s string) string {
|
|
|
|
|
// 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 00:03:59 -07:00
|
|
|
return Map(
|
2011-10-25 22:22:09 -07:00
|
|
|
func(r rune) rune {
|
2010-07-20 00:03:59 -07:00
|
|
|
if isSeparator(prev) {
|
|
|
|
|
prev = r
|
|
|
|
|
return unicode.ToTitle(r)
|
|
|
|
|
}
|
|
|
|
|
prev = r
|
|
|
|
|
return r
|
|
|
|
|
},
|
|
|
|
|
s)
|
|
|
|
|
}
|
|
|
|
|
|
2010-04-09 18:57:03 -07:00
|
|
|
// TrimLeftFunc returns a slice of the string s with all leading
|
|
|
|
|
// Unicode code points c satisfying f(c) removed.
|
2011-10-25 22:22:09 -07:00
|
|
|
func TrimLeftFunc(s string, f func(rune) bool) string {
|
2010-06-14 14:54:48 -07:00
|
|
|
i := indexFunc(s, f, false)
|
|
|
|
|
if i == -1 {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return s[i:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimRightFunc returns a slice of the string s with all trailing
|
|
|
|
|
// Unicode code points c satisfying f(c) removed.
|
2011-10-25 22:22:09 -07:00
|
|
|
func TrimRightFunc(s string, f func(rune) bool) string {
|
2010-06-14 14:54:48 -07:00
|
|
|
i := lastIndexFunc(s, f, false)
|
|
|
|
|
if i >= 0 && s[i] >= utf8.RuneSelf {
|
|
|
|
|
_, wid := utf8.DecodeRuneInString(s[i:])
|
|
|
|
|
i += wid
|
|
|
|
|
} else {
|
|
|
|
|
i++
|
|
|
|
|
}
|
|
|
|
|
return s[0:i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimFunc returns a slice of the string s with all leading
|
|
|
|
|
// and trailing Unicode code points c satisfying f(c) removed.
|
2011-10-25 22:22:09 -07:00
|
|
|
func TrimFunc(s string, f func(rune) bool) string {
|
2010-06-14 14:54:48 -07:00
|
|
|
return TrimRightFunc(TrimLeftFunc(s, f), f)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IndexFunc returns the index into 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 string, f func(rune) bool) int {
|
2010-06-14 14:54:48 -07:00
|
|
|
return indexFunc(s, f, true)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LastIndexFunc returns the index into 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 string, f func(rune) bool) int {
|
2010-06-14 14:54:48 -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
|
2010-07-23 12:34:35 -07:00
|
|
|
// inverted.
|
2011-10-25 22:22:09 -07:00
|
|
|
func indexFunc(s string, f func(rune) bool, truth bool) int {
|
2016-11-08 17:54:30 +11:00
|
|
|
for i, r := range s {
|
2011-10-25 22:22:09 -07:00
|
|
|
if f(r) == truth {
|
2016-11-08 17:54:30 +11:00
|
|
|
return i
|
2009-09-01 11:06:28 -07:00
|
|
|
}
|
2009-06-05 13:09:03 -07:00
|
|
|
}
|
2010-06-14 14:54:48 -07:00
|
|
|
return -1
|
2010-04-09 18:57:03 -07:00
|
|
|
}
|
|
|
|
|
|
2010-06-14 14:54:48 -07:00
|
|
|
// lastIndexFunc is the same as LastIndexFunc except that if
|
|
|
|
|
// truth==false, the sense of the predicate function is
|
2010-07-23 12:34:35 -07:00
|
|
|
// inverted.
|
2011-10-25 22:22:09 -07:00
|
|
|
func lastIndexFunc(s string, f func(rune) bool, truth bool) int {
|
2010-09-23 20:40:11 +10:00
|
|
|
for i := len(s); i > 0; {
|
2011-10-25 22:22:09 -07:00
|
|
|
r, size := utf8.DecodeLastRuneInString(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 11:06:28 -07:00
|
|
|
}
|
2009-06-05 13:09:03 -07:00
|
|
|
}
|
2010-06-14 14:54:48 -07:00
|
|
|
return -1
|
2010-04-09 18:57:03 -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(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-11-23 20:20:14 -08:00
|
|
|
return func(r rune) bool { return IndexRune(cutset, r) >= 0 }
|
2010-04-09 18:57:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Trim returns a slice of the string s with all leading and
|
|
|
|
|
// trailing Unicode code points contained in cutset removed.
|
|
|
|
|
func Trim(s string, cutset string) string {
|
|
|
|
|
if s == "" || cutset == "" {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
return TrimFunc(s, makeCutsetFunc(cutset))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimLeft returns a slice of the string s with all leading
|
|
|
|
|
// Unicode code points contained in cutset removed.
|
2018-06-29 17:46:17 +01:00
|
|
|
//
|
|
|
|
|
// To remove a prefix, use TrimPrefix instead.
|
2010-04-09 18:57:03 -07:00
|
|
|
func TrimLeft(s string, cutset string) string {
|
|
|
|
|
if s == "" || cutset == "" {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
return TrimLeftFunc(s, makeCutsetFunc(cutset))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimRight returns a slice of the string s, with all trailing
|
|
|
|
|
// Unicode code points contained in cutset removed.
|
2018-06-29 17:46:17 +01:00
|
|
|
//
|
|
|
|
|
// To remove a suffix, use TrimSuffix instead.
|
2010-04-09 18:57:03 -07:00
|
|
|
func TrimRight(s string, cutset string) string {
|
|
|
|
|
if s == "" || cutset == "" {
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
return TrimRightFunc(s, makeCutsetFunc(cutset))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TrimSpace returns a slice of the string s, with all leading
|
|
|
|
|
// and trailing white space removed, as defined by Unicode.
|
|
|
|
|
func TrimSpace(s string) string {
|
|
|
|
|
return TrimFunc(s, unicode.IsSpace)
|
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 string) string {
|
|
|
|
|
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 string) string {
|
|
|
|
|
if HasSuffix(s, suffix) {
|
|
|
|
|
return s[:len(s)-len(suffix)]
|
|
|
|
|
}
|
|
|
|
|
return s
|
2009-06-05 13:09:03 -07:00
|
|
|
}
|
2010-06-30 18:03:09 -07:00
|
|
|
|
|
|
|
|
// Replace returns a copy of the string 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 string
|
|
|
|
|
// and after each UTF-8 sequence, yielding up to k+1 replacements
|
|
|
|
|
// for a k-rune string.
|
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 string, n int) string {
|
2010-07-01 14:08:14 -07:00
|
|
|
if old == new || n == 0 {
|
2010-06-30 18:03:09 -07:00
|
|
|
return s // avoid allocation
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute number of replacements.
|
|
|
|
|
if m := Count(s, old); m == 0 {
|
|
|
|
|
return s // avoid allocation
|
2010-07-01 14:08:14 -07:00
|
|
|
} else 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.DecodeRuneInString(s[start:])
|
|
|
|
|
j += wid
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
j += Index(s[start:], old)
|
|
|
|
|
}
|
2010-10-26 21:52:54 -07:00
|
|
|
w += copy(t[w:], s[start:j])
|
|
|
|
|
w += copy(t[w:], new)
|
2010-06-30 18:03:09 -07:00
|
|
|
start = j + len(old)
|
|
|
|
|
}
|
2010-10-26 21:52:54 -07:00
|
|
|
w += copy(t[w:], s[start:])
|
2010-06-30 18:03:09 -07:00
|
|
|
return string(t[0:w])
|
|
|
|
|
}
|
2011-09-26 18:32:51 -04:00
|
|
|
|
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.
|
2011-09-26 18:32:51 -04:00
|
|
|
func EqualFold(s, t string) bool {
|
|
|
|
|
for s != "" && t != "" {
|
|
|
|
|
// Extract first rune from each string.
|
2011-10-25 22:22:09 -07:00
|
|
|
var sr, tr rune
|
2011-09-26 18:32:51 -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 18:32:51 -04:00
|
|
|
} else {
|
|
|
|
|
r, size := utf8.DecodeRuneInString(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 18:32:51 -04:00
|
|
|
} else {
|
|
|
|
|
r, size := utf8.DecodeRuneInString(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.
|
2018-04-29 00:15:03 -04:00
|
|
|
if tr < utf8.RuneSelf {
|
|
|
|
|
// ASCII only, sr/tr must be upper/lower case
|
|
|
|
|
if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' {
|
2011-09-26 18:32:51 -04:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 23:21:55 +00:00
|
|
|
// General case. SimpleFold(x) returns the next equivalent rune > x
|
2011-09-26 18:32:51 -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 18:32:51 -04:00
|
|
|
return s == t
|
|
|
|
|
}
|
2017-11-04 10:19:53 -07:00
|
|
|
|
2018-03-04 09:47:47 -08:00
|
|
|
// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.
|
|
|
|
|
func Index(s, substr string) int {
|
|
|
|
|
n := len(substr)
|
|
|
|
|
switch {
|
|
|
|
|
case n == 0:
|
|
|
|
|
return 0
|
|
|
|
|
case n == 1:
|
|
|
|
|
return IndexByte(s, substr[0])
|
|
|
|
|
case n == len(s):
|
|
|
|
|
if substr == s {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
case n > len(s):
|
|
|
|
|
return -1
|
|
|
|
|
case n <= bytealg.MaxLen:
|
|
|
|
|
// Use brute force when s and substr both are small
|
|
|
|
|
if len(s) <= bytealg.MaxBruteForce {
|
|
|
|
|
return bytealg.IndexString(s, substr)
|
|
|
|
|
}
|
|
|
|
|
c := substr[0]
|
|
|
|
|
i := 0
|
|
|
|
|
t := s[:len(s)-n+1]
|
|
|
|
|
fails := 0
|
|
|
|
|
for i < len(t) {
|
|
|
|
|
if t[i] != c {
|
|
|
|
|
// IndexByte is faster than bytealg.IndexString, so use it as long as
|
|
|
|
|
// we're not getting lots of false positives.
|
|
|
|
|
o := IndexByte(t[i:], c)
|
|
|
|
|
if o < 0 {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
i += o
|
|
|
|
|
}
|
|
|
|
|
if s[i:i+n] == substr {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
fails++
|
|
|
|
|
i++
|
|
|
|
|
// Switch to bytealg.IndexString when IndexByte produces too many false positives.
|
|
|
|
|
if fails > bytealg.Cutover(i) {
|
|
|
|
|
r := bytealg.IndexString(s[i:], substr)
|
|
|
|
|
if r >= 0 {
|
|
|
|
|
return r + i
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
c := substr[0]
|
|
|
|
|
i := 0
|
|
|
|
|
t := s[:len(s)-n+1]
|
|
|
|
|
fails := 0
|
|
|
|
|
for i < len(t) {
|
|
|
|
|
if t[i] != c {
|
|
|
|
|
o := IndexByte(t[i:], c)
|
|
|
|
|
if o < 0 {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
i += o
|
|
|
|
|
}
|
|
|
|
|
if s[i:i+n] == substr {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
i++
|
|
|
|
|
fails++
|
|
|
|
|
if fails >= 4+i>>4 && i < len(t) {
|
|
|
|
|
// See comment in ../bytes/bytes_generic.go.
|
|
|
|
|
j := indexRabinKarp(s[i:], substr)
|
|
|
|
|
if j < 0 {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
return i + j
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-04 10:19:53 -07:00
|
|
|
func indexRabinKarp(s, substr string) int {
|
|
|
|
|
// Rabin-Karp search
|
|
|
|
|
hashss, pow := hashStr(substr)
|
|
|
|
|
n := len(substr)
|
|
|
|
|
var h uint32
|
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
|
h = h*primeRK + uint32(s[i])
|
|
|
|
|
}
|
|
|
|
|
if h == hashss && s[:n] == substr {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
for i := n; i < len(s); {
|
|
|
|
|
h *= primeRK
|
|
|
|
|
h += uint32(s[i])
|
|
|
|
|
h -= pow * uint32(s[i-n])
|
|
|
|
|
i++
|
|
|
|
|
if h == hashss && s[i-n:i] == substr {
|
|
|
|
|
return i - n
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
|
|
|
|
|
}
|