mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
all: replace strings.Split with strings.SplitSeq
In Go 1.25+, strings.SplitSeq offers better
performance. Here are the benchmark results comparing
strings.Split and strings.SplitSeq in a for-loop, with the
benchmark code located in src/strings/iter_test.go:
goos: darwin
goarch: amd64
pkg: cmd/go/internal/auth
cpu: Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz
│ old.txt │ new.txt │
│ sec/op │ sec/op vs base │
ParseGitAuth/standard-8 281.4n ± 1% 218.0n ± 11% -22.54% (p=0.000 n=10)
ParseGitAuth/with_url-8 549.1n ± 1% 480.5n ± 13% -12.48% (p=0.002 n=10)
ParseGitAuth/minimal-8 235.4n ± 1% 197.3n ± 7% -16.20% (p=0.000 n=10)
ParseGitAuth/complex-8 797.6n ± 2% 805.2n ± 4% ~ (p=0.481 n=10)
ParseGitAuth/empty-8 87.48n ± 3% 63.25n ± 6% -27.71% (p=0.000 n=10)
ParseGitAuth/malformed-8 228.8n ± 1% 171.2n ± 3% -25.17% (p=0.000 n=10)
geomean 288.9n 237.7n -17.72%
│ old.txt │ new.txt │
│ B/op │ B/op vs base │
ParseGitAuth/standard-8 192.00 ± 0% 96.00 ± 0% -50.00% (p=0.000 n=10)
ParseGitAuth/with_url-8 400.0 ± 0% 288.0 ± 0% -28.00% (p=0.000 n=10)
ParseGitAuth/minimal-8 144.00 ± 0% 80.00 ± 0% -44.44% (p=0.000 n=10)
ParseGitAuth/complex-8 528.0 ± 0% 400.0 ± 0% -24.24% (p=0.000 n=10)
ParseGitAuth/empty-8 32.00 ± 0% 16.00 ± 0% -50.00% (p=0.000 n=10)
ParseGitAuth/malformed-8 176.00 ± 0% 80.00 ± 0% -54.55% (p=0.000 n=10)
geomean 179.0 102.1 -42.96%
│ old.txt │ new.txt │
│ allocs/op │ allocs/op vs base │
ParseGitAuth/standard-8 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.000 n=10)
ParseGitAuth/with_url-8 4.000 ± 0% 3.000 ± 0% -25.00% (p=0.000 n=10)
ParseGitAuth/minimal-8 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.000 n=10)
ParseGitAuth/complex-8 4.000 ± 0% 3.000 ± 0% -25.00% (p=0.000 n=10)
ParseGitAuth/empty-8 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.000 n=10)
ParseGitAuth/malformed-8 3.000 ± 0% 2.000 ± 0% -33.33% (p=0.000 n=10)
geomean 3.086 2.040 -33.91%
Updates #69315.
Change-Id: Id0219edea45d9658d527b863162ebe917e7821d9
GitHub-Last-Rev: 392b315e12
GitHub-Pull-Request: golang/go#75259
Reviewed-on: https://go-review.googlesource.com/c/go/+/701015
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
Reviewed-by: Keith Randall <khr@google.com>
Auto-Submit: Emmanuel Odeke <emmanuel@orijtech.com>
This commit is contained in:
parent
10bfddc91d
commit
e3ed0fbe6a
39 changed files with 178 additions and 50 deletions
|
|
@ -212,7 +212,7 @@ func copyLocalData(dstbase string) (pkgpath string, err error) {
|
||||||
// Copy all immediate files and testdata directories between
|
// Copy all immediate files and testdata directories between
|
||||||
// the package being tested and the source root.
|
// the package being tested and the source root.
|
||||||
pkgpath = ""
|
pkgpath = ""
|
||||||
for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
|
for element := range strings.SplitSeq(finalPkgpath, string(filepath.Separator)) {
|
||||||
if debug {
|
if debug {
|
||||||
log.Printf("copying %s", pkgpath)
|
log.Printf("copying %s", pkgpath)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ func (f *File) loadDefines(gccOptions []string) bool {
|
||||||
stdout := gccDefines(b.Bytes(), gccOptions)
|
stdout := gccDefines(b.Bytes(), gccOptions)
|
||||||
|
|
||||||
var gccIsClang bool
|
var gccIsClang bool
|
||||||
for _, line := range strings.Split(stdout, "\n") {
|
for line := range strings.SplitSeq(stdout, "\n") {
|
||||||
if len(line) < 9 || line[0:7] != "#define" {
|
if len(line) < 9 || line[0:7] != "#define" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -428,7 +428,7 @@ func (p *Package) guessKinds(f *File) []*Name {
|
||||||
notDeclared
|
notDeclared
|
||||||
)
|
)
|
||||||
sawUnmatchedErrors := false
|
sawUnmatchedErrors := false
|
||||||
for _, line := range strings.Split(stderr, "\n") {
|
for line := range strings.SplitSeq(stderr, "\n") {
|
||||||
// Ignore warnings and random comments, with one
|
// Ignore warnings and random comments, with one
|
||||||
// exception: newer GCC versions will sometimes emit
|
// exception: newer GCC versions will sometimes emit
|
||||||
// an error on a macro #define with a note referring
|
// an error on a macro #define with a note referring
|
||||||
|
|
|
||||||
|
|
@ -447,7 +447,7 @@ func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
buf := make([]byte, 65536)
|
buf := make([]byte, 65536)
|
||||||
trace := string(buf[:runtime.Stack(buf, true)])
|
trace := string(buf[:runtime.Stack(buf, true)])
|
||||||
for _, goroutine := range strings.Split(trace, "\n\n") {
|
for goroutine := range strings.SplitSeq(trace, "\n\n") {
|
||||||
if strings.Contains(goroutine, "test.issue7978go") {
|
if strings.Contains(goroutine, "test.issue7978go") {
|
||||||
trace := strings.Split(goroutine, "\n")
|
trace := strings.Split(goroutine, "\n")
|
||||||
// look for the expected function in the stack
|
// look for the expected function in the stack
|
||||||
|
|
|
||||||
|
|
@ -570,7 +570,7 @@ func readEmbedCfg(file string) {
|
||||||
|
|
||||||
// parseSpectre parses the spectre configuration from the string s.
|
// parseSpectre parses the spectre configuration from the string s.
|
||||||
func parseSpectre(s string) {
|
func parseSpectre(s string) {
|
||||||
for _, f := range strings.Split(s, ",") {
|
for f := range strings.SplitSeq(s, ",") {
|
||||||
f = strings.TrimSpace(f)
|
f = strings.TrimSpace(f)
|
||||||
switch f {
|
switch f {
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
6
src/cmd/dist/build.go
vendored
6
src/cmd/dist/build.go
vendored
|
|
@ -380,7 +380,7 @@ func findgoversion() string {
|
||||||
if i := strings.Index(b, "\n"); i >= 0 {
|
if i := strings.Index(b, "\n"); i >= 0 {
|
||||||
rest := b[i+1:]
|
rest := b[i+1:]
|
||||||
b = chomp(b[:i])
|
b = chomp(b[:i])
|
||||||
for _, line := range strings.Split(rest, "\n") {
|
for line := range strings.SplitSeq(rest, "\n") {
|
||||||
f := strings.Fields(line)
|
f := strings.Fields(line)
|
||||||
if len(f) == 0 {
|
if len(f) == 0 {
|
||||||
continue
|
continue
|
||||||
|
|
@ -1137,7 +1137,7 @@ func shouldbuild(file, pkg string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check file contents for //go:build lines.
|
// Check file contents for //go:build lines.
|
||||||
for _, p := range strings.Split(readfile(file), "\n") {
|
for p := range strings.SplitSeq(readfile(file), "\n") {
|
||||||
p = strings.TrimSpace(p)
|
p = strings.TrimSpace(p)
|
||||||
if p == "" {
|
if p == "" {
|
||||||
continue
|
continue
|
||||||
|
|
@ -2016,7 +2016,7 @@ func cmdlist() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setNoOpt() {
|
func setNoOpt() {
|
||||||
for _, gcflag := range strings.Split(gogcflags, " ") {
|
for gcflag := range strings.SplitSeq(gogcflags, " ") {
|
||||||
if gcflag == "-N" || gcflag == "-l" {
|
if gcflag == "-N" || gcflag == "-l" {
|
||||||
noOpt = true
|
noOpt = true
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ func readVERSION(goroot string) (version string, t time.Time) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
version, rest, _ := strings.Cut(string(data), "\n")
|
version, rest, _ := strings.Cut(string(data), "\n")
|
||||||
for _, line := range strings.Split(rest, "\n") {
|
for line := range strings.SplitSeq(rest, "\n") {
|
||||||
f := strings.Fields(line)
|
f := strings.Fields(line)
|
||||||
if len(f) == 0 {
|
if len(f) == 0 {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -84,14 +84,14 @@ func main() {
|
||||||
|
|
||||||
if *allowedRewrites != "" {
|
if *allowedRewrites != "" {
|
||||||
allowed = make(map[string]bool)
|
allowed = make(map[string]bool)
|
||||||
for _, f := range strings.Split(*allowedRewrites, ",") {
|
for f := range strings.SplitSeq(*allowedRewrites, ",") {
|
||||||
allowed[f] = true
|
allowed[f] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *forceRewrites != "" {
|
if *forceRewrites != "" {
|
||||||
force = make(map[string]bool)
|
force = make(map[string]bool)
|
||||||
for _, f := range strings.Split(*forceRewrites, ",") {
|
for f := range strings.SplitSeq(*forceRewrites, ",") {
|
||||||
force[f] = true
|
force[f] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ func runGitAuth(client *http.Client, dir, url string) (string, http.Header, erro
|
||||||
// Any of these values may be empty if parsing fails.
|
// Any of these values may be empty if parsing fails.
|
||||||
func parseGitAuth(data []byte) (parsedPrefix, username, password string) {
|
func parseGitAuth(data []byte) (parsedPrefix, username, password string) {
|
||||||
prefix := new(url.URL)
|
prefix := new(url.URL)
|
||||||
for _, line := range strings.Split(string(data), "\n") {
|
for line := range strings.SplitSeq(string(data), "\n") {
|
||||||
key, value, ok := strings.Cut(strings.TrimSpace(line), "=")
|
key, value, ok := strings.Cut(strings.TrimSpace(line), "=")
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -82,3 +83,77 @@ password:secr3t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkParseGitAuth(b *testing.B) {
|
||||||
|
// Define different test scenarios to benchmark
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
data []byte
|
||||||
|
}{{
|
||||||
|
// Standard scenario with all basic fields present
|
||||||
|
name: "standard",
|
||||||
|
data: []byte(`
|
||||||
|
protocol=https
|
||||||
|
host=example.com
|
||||||
|
username=bob
|
||||||
|
password=secr3t
|
||||||
|
`),
|
||||||
|
}, {
|
||||||
|
// Scenario with URL field included
|
||||||
|
name: "with_url",
|
||||||
|
data: []byte(`
|
||||||
|
protocol=https
|
||||||
|
host=example.com
|
||||||
|
username=bob
|
||||||
|
password=secr3t
|
||||||
|
url=https://example.com/repo
|
||||||
|
`),
|
||||||
|
}, {
|
||||||
|
// Minimal scenario with only required fields
|
||||||
|
name: "minimal",
|
||||||
|
data: []byte(`
|
||||||
|
protocol=https
|
||||||
|
host=example.com
|
||||||
|
`),
|
||||||
|
}, {
|
||||||
|
// Complex scenario with longer values and extra fields
|
||||||
|
name: "complex",
|
||||||
|
data: func() []byte {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString("protocol=https\n")
|
||||||
|
builder.WriteString("host=example.com\n")
|
||||||
|
builder.WriteString("username=longusernamenamename\n")
|
||||||
|
builder.WriteString("password=longpasswordwithmanycharacters123456789\n")
|
||||||
|
builder.WriteString("url=https://example.com/very/long/path/to/repository\n")
|
||||||
|
builder.WriteString("extra1=value1\n")
|
||||||
|
builder.WriteString("extra2=value2\n")
|
||||||
|
return []byte(builder.String())
|
||||||
|
}(),
|
||||||
|
}, {
|
||||||
|
// Scenario with empty input
|
||||||
|
name: "empty",
|
||||||
|
data: []byte(``),
|
||||||
|
}, {
|
||||||
|
// Scenario with malformed input (using colon instead of equals)
|
||||||
|
name: "malformed",
|
||||||
|
data: []byte(`
|
||||||
|
protocol:https
|
||||||
|
host:example.com
|
||||||
|
username:bob
|
||||||
|
password:secr3t
|
||||||
|
`),
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
b.Run(tc.name, func(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
for b.Loop() {
|
||||||
|
prefix, username, password := parseGitAuth(tc.data)
|
||||||
|
|
||||||
|
_ = prefix
|
||||||
|
_ = username
|
||||||
|
_ = password
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ func parseNetrc(data string) []netrcLine {
|
||||||
var nrc []netrcLine
|
var nrc []netrcLine
|
||||||
var l netrcLine
|
var l netrcLine
|
||||||
inMacro := false
|
inMacro := false
|
||||||
for _, line := range strings.Split(data, "\n") {
|
for line := range strings.SplitSeq(data, "\n") {
|
||||||
if inMacro {
|
if inMacro {
|
||||||
if line == "" {
|
if line == "" {
|
||||||
inMacro = false
|
inMacro = false
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ func findCodeRoots() []Dir {
|
||||||
cmd := exec.Command(goCmd(), "list", "-m", "-f={{.Path}}\t{{.Dir}}", "all")
|
cmd := exec.Command(goCmd(), "list", "-m", "-f={{.Path}}\t{{.Dir}}", "all")
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
out, _ := cmd.Output()
|
out, _ := cmd.Output()
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
for line := range strings.SplitSeq(string(out), "\n") {
|
||||||
path, dir, _ := strings.Cut(line, "\t")
|
path, dir, _ := strings.Cut(line, "\t")
|
||||||
if dir != "" {
|
if dir != "" {
|
||||||
list = append(list, Dir{importPath: path, dir: dir, inModule: true})
|
list = append(list, Dir{importPath: path, dir: dir, inModule: true})
|
||||||
|
|
|
||||||
|
|
@ -920,7 +920,7 @@ func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldLis
|
||||||
|
|
||||||
start := doc.List[0].Slash
|
start := doc.List[0].Slash
|
||||||
doc.List = doc.List[:0]
|
doc.List = doc.List[:0]
|
||||||
for _, line := range strings.Split(text, "\n") {
|
for line := range strings.SplitSeq(text, "\n") {
|
||||||
prefix := "// "
|
prefix := "// "
|
||||||
if len(line) > 0 && line[0] == '\t' {
|
if len(line) > 0 && line[0] == '\t' {
|
||||||
prefix = "//"
|
prefix = "//"
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ func (v *jsonFlag) Set(s string) error {
|
||||||
if *v == nil {
|
if *v == nil {
|
||||||
*v = make(map[string]bool)
|
*v = make(map[string]bool)
|
||||||
}
|
}
|
||||||
for _, f := range strings.Split(s, ",") {
|
for f := range strings.SplitSeq(s, ",") {
|
||||||
(*v)[f] = true
|
(*v)[f] = true
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ func (r *gitRepo) loadLocalTags(ctx context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
for line := range strings.SplitSeq(string(out), "\n") {
|
||||||
if line != "" {
|
if line != "" {
|
||||||
r.localTags.Store(line, true)
|
r.localTags.Store(line, true)
|
||||||
}
|
}
|
||||||
|
|
@ -273,7 +273,7 @@ func (r *gitRepo) loadRefs(ctx context.Context) (map[string]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
refs := make(map[string]string)
|
refs := make(map[string]string)
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
for line := range strings.SplitSeq(string(out), "\n") {
|
||||||
f := strings.Fields(line)
|
f := strings.Fields(line)
|
||||||
if len(f) != 2 {
|
if len(f) != 2 {
|
||||||
continue
|
continue
|
||||||
|
|
@ -745,7 +745,7 @@ func (r *gitRepo) RecentTag(ctx context.Context, rev, prefix string, allowed fun
|
||||||
|
|
||||||
// prefixed tags aren't valid semver tags so compare without prefix, but only tags with correct prefix
|
// prefixed tags aren't valid semver tags so compare without prefix, but only tags with correct prefix
|
||||||
var highest string
|
var highest string
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
for line := range strings.SplitSeq(string(out), "\n") {
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
// git do support lstrip in for-each-ref format, but it was added in v2.13.0. Stripping here
|
// git do support lstrip in for-each-ref format, but it was added in v2.13.0. Stripping here
|
||||||
// instead gives support for git v2.7.0.
|
// instead gives support for git v2.7.0.
|
||||||
|
|
|
||||||
|
|
@ -561,7 +561,7 @@ func bzrParseStat(rev, out string) (*RevInfo, error) {
|
||||||
var revno int64
|
var revno int64
|
||||||
var tm time.Time
|
var tm time.Time
|
||||||
var tags []string
|
var tags []string
|
||||||
for _, line := range strings.Split(out, "\n") {
|
for line := range strings.SplitSeq(out, "\n") {
|
||||||
if line == "" || line[0] == ' ' || line[0] == '\t' {
|
if line == "" || line[0] == ' ' || line[0] == '\t' {
|
||||||
// End of header, start of commit message.
|
// End of header, start of commit message.
|
||||||
break
|
break
|
||||||
|
|
@ -614,7 +614,7 @@ func bzrParseStat(rev, out string) (*RevInfo, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fossilParseStat(rev, out string) (*RevInfo, error) {
|
func fossilParseStat(rev, out string) (*RevInfo, error) {
|
||||||
for _, line := range strings.Split(out, "\n") {
|
for line := range strings.SplitSeq(out, "\n") {
|
||||||
if strings.HasPrefix(line, "uuid:") || strings.HasPrefix(line, "hash:") {
|
if strings.HasPrefix(line, "uuid:") || strings.HasPrefix(line, "hash:") {
|
||||||
f := strings.Fields(line)
|
f := strings.Fields(line)
|
||||||
if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" {
|
if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" {
|
||||||
|
|
|
||||||
|
|
@ -426,7 +426,7 @@ Lines:
|
||||||
// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
|
// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
|
||||||
// that affect the way cgo's C code is built.
|
// that affect the way cgo's C code is built.
|
||||||
func (ctxt *Context) saveCgo(filename string, di *build.Package, text string) error {
|
func (ctxt *Context) saveCgo(filename string, di *build.Package, text string) error {
|
||||||
for _, line := range strings.Split(text, "\n") {
|
for line := range strings.SplitSeq(text, "\n") {
|
||||||
orig := line
|
orig := line
|
||||||
|
|
||||||
// Line is
|
// Line is
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@ func importRaw(modroot, reldir string) *rawPackage {
|
||||||
// which is the comment on import "C".
|
// which is the comment on import "C".
|
||||||
func extractCgoDirectives(doc string) []string {
|
func extractCgoDirectives(doc string) []string {
|
||||||
var out []string
|
var out []string
|
||||||
for _, line := range strings.Split(doc, "\n") {
|
for line := range strings.SplitSeq(doc, "\n") {
|
||||||
// Line is
|
// Line is
|
||||||
// #cgo [GOOS/GOARCH...] LDFLAGS: stuff
|
// #cgo [GOOS/GOARCH...] LDFLAGS: stuff
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ func readModGraph(ctx context.Context, pruning modPruning, roots []module.Versio
|
||||||
// so it wouldn't be useful to log when that occurs (because it happens in
|
// so it wouldn't be useful to log when that occurs (because it happens in
|
||||||
// normal operation all the time).
|
// normal operation all the time).
|
||||||
readModGraphDebugOnce.Do(func() {
|
readModGraphDebugOnce.Do(func() {
|
||||||
for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") {
|
for f := range strings.SplitSeq(os.Getenv("GODEBUG"), ",") {
|
||||||
switch f {
|
switch f {
|
||||||
case "lazymod=log":
|
case "lazymod=log":
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
|
|
|
||||||
|
|
@ -1597,7 +1597,7 @@ func modulesTextIsForWorkspace(vendorDir string) (bool, error) {
|
||||||
}
|
}
|
||||||
line, _, _ := strings.Cut(string(buf[:n]), "\n")
|
line, _, _ := strings.Cut(string(buf[:n]), "\n")
|
||||||
if annotations, ok := strings.CutPrefix(line, "## "); ok {
|
if annotations, ok := strings.CutPrefix(line, "## "); ok {
|
||||||
for _, entry := range strings.Split(annotations, ";") {
|
for entry := range strings.SplitSeq(annotations, ";") {
|
||||||
entry = strings.TrimSpace(entry)
|
entry = strings.TrimSpace(entry)
|
||||||
if entry == "workspace" {
|
if entry == "workspace" {
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ func readVendorList(vendorDir string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var mod module.Version
|
var mod module.Version
|
||||||
for _, line := range strings.Split(string(data), "\n") {
|
for line := range strings.SplitSeq(string(data), "\n") {
|
||||||
if strings.HasPrefix(line, "# ") {
|
if strings.HasPrefix(line, "# ") {
|
||||||
f := strings.Fields(line)
|
f := strings.Fields(line)
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ func readVendorList(vendorDir string) {
|
||||||
if annotations, ok := strings.CutPrefix(line, "## "); ok {
|
if annotations, ok := strings.CutPrefix(line, "## "); ok {
|
||||||
// Metadata. Take the union of annotations across multiple lines, if present.
|
// Metadata. Take the union of annotations across multiple lines, if present.
|
||||||
meta := vendorMeta[mod]
|
meta := vendorMeta[mod]
|
||||||
for _, entry := range strings.Split(annotations, ";") {
|
for entry := range strings.SplitSeq(annotations, ";") {
|
||||||
entry = strings.TrimSpace(entry)
|
entry = strings.TrimSpace(entry)
|
||||||
if entry == "explicit" {
|
if entry == "explicit" {
|
||||||
meta.Explicit = true
|
meta.Explicit = true
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ func (f *vetFlag) Set(value string) error {
|
||||||
|
|
||||||
*f = vetFlag{explicit: true}
|
*f = vetFlag{explicit: true}
|
||||||
var single string
|
var single string
|
||||||
for _, arg := range strings.Split(value, ",") {
|
for arg := range strings.SplitSeq(value, ",") {
|
||||||
switch arg {
|
switch arg {
|
||||||
case "":
|
case "":
|
||||||
return fmt.Errorf("-vet argument contains empty list element")
|
return fmt.Errorf("-vet argument contains empty list element")
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ var pathExts = sync.OnceValue(func() []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
var exts []string
|
var exts []string
|
||||||
for _, e := range strings.Split(strings.ToLower(x), `;`) {
|
for e := range strings.SplitSeq(strings.ToLower(x), `;`) {
|
||||||
if e == "" {
|
if e == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ func (v *Cmd) isSecureScheme(scheme string) bool {
|
||||||
// colon-separated list of schemes that are allowed to be used with git
|
// colon-separated list of schemes that are allowed to be used with git
|
||||||
// fetch/clone. Any scheme not mentioned will be considered insecure.
|
// fetch/clone. Any scheme not mentioned will be considered insecure.
|
||||||
if allow := os.Getenv("GIT_ALLOW_PROTOCOL"); allow != "" {
|
if allow := os.Getenv("GIT_ALLOW_PROTOCOL"); allow != "" {
|
||||||
for _, s := range strings.Split(allow, ":") {
|
for s := range strings.SplitSeq(allow, ":") {
|
||||||
if s == scheme {
|
if s == scheme {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -440,7 +440,7 @@ func bzrStatus(vcsBzr *Cmd, rootDir string) (Status, error) {
|
||||||
var rev string
|
var rev string
|
||||||
var commitTime time.Time
|
var commitTime time.Time
|
||||||
|
|
||||||
for _, line := range strings.Split(out, "\n") {
|
for line := range strings.SplitSeq(out, "\n") {
|
||||||
i := strings.IndexByte(line, ':')
|
i := strings.IndexByte(line, ':')
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
continue
|
continue
|
||||||
|
|
@ -974,7 +974,7 @@ func parseGOVCS(s string) (govcsConfig, error) {
|
||||||
}
|
}
|
||||||
var cfg govcsConfig
|
var cfg govcsConfig
|
||||||
have := make(map[string]string)
|
have := make(map[string]string)
|
||||||
for _, item := range strings.Split(s, ",") {
|
for item := range strings.SplitSeq(s, ",") {
|
||||||
item = strings.TrimSpace(item)
|
item = strings.TrimSpace(item)
|
||||||
if item == "" {
|
if item == "" {
|
||||||
return nil, fmt.Errorf("empty entry in GOVCS")
|
return nil, fmt.Errorf("empty entry in GOVCS")
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
// uniqueness: if a path exists as a directory, then it cannot exist as a
|
// uniqueness: if a path exists as a directory, then it cannot exist as a
|
||||||
// ".txt" script (because the search would ignore that file).
|
// ".txt" script (because the search would ignore that file).
|
||||||
scriptPath := "."
|
scriptPath := "."
|
||||||
for _, part := range strings.Split(clean, "/") {
|
for part := range strings.SplitSeq(clean, "/") {
|
||||||
scriptPath = filepath.Join(scriptPath, part)
|
scriptPath = filepath.Join(scriptPath, part)
|
||||||
dir := filepath.Join(s.scriptDir, scriptPath)
|
dir := filepath.Join(s.scriptDir, scriptPath)
|
||||||
if _, err := os.Stat(dir); err != nil {
|
if _, err := os.Stat(dir); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -384,7 +384,7 @@ func (v *tagsFlag) Set(s string) error {
|
||||||
|
|
||||||
// Split on commas, ignore empty strings.
|
// Split on commas, ignore empty strings.
|
||||||
*v = []string{}
|
*v = []string{}
|
||||||
for _, s := range strings.Split(s, ",") {
|
for s := range strings.SplitSeq(s, ",") {
|
||||||
if s != "" {
|
if s != "" {
|
||||||
*v = append(*v, s)
|
*v = append(*v, s)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1122,7 +1122,7 @@ func (b *Builder) loadCachedVet(a *Action) error {
|
||||||
return fmt.Errorf("reading srcfiles list: %w", err)
|
return fmt.Errorf("reading srcfiles list: %w", err)
|
||||||
}
|
}
|
||||||
var srcfiles []string
|
var srcfiles []string
|
||||||
for _, name := range strings.Split(string(list), "\n") {
|
for name := range strings.SplitSeq(string(list), "\n") {
|
||||||
if name == "" { // end of list
|
if name == "" { // end of list
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -1146,7 +1146,7 @@ func (b *Builder) loadCachedCompiledGoFiles(a *Action) error {
|
||||||
return fmt.Errorf("reading srcfiles list: %w", err)
|
return fmt.Errorf("reading srcfiles list: %w", err)
|
||||||
}
|
}
|
||||||
var gofiles []string
|
var gofiles []string
|
||||||
for _, name := range strings.Split(string(list), "\n") {
|
for name := range strings.SplitSeq(string(list), "\n") {
|
||||||
if name == "" { // end of list
|
if name == "" { // end of list
|
||||||
continue
|
continue
|
||||||
} else if !strings.HasSuffix(name, ".go") {
|
} else if !strings.HasSuffix(name, ".go") {
|
||||||
|
|
|
||||||
|
|
@ -278,7 +278,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
const ldflagsPrefix = "_CGO_LDFLAGS="
|
const ldflagsPrefix = "_CGO_LDFLAGS="
|
||||||
for _, line := range strings.Split(string(flags), "\n") {
|
for line := range strings.SplitSeq(string(flags), "\n") {
|
||||||
if strings.HasPrefix(line, ldflagsPrefix) {
|
if strings.HasPrefix(line, ldflagsPrefix) {
|
||||||
flag := line[len(ldflagsPrefix):]
|
flag := line[len(ldflagsPrefix):]
|
||||||
// Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
|
// Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
|
||||||
|
|
|
||||||
|
|
@ -277,7 +277,7 @@ func (f *DebugFlag) Set(debugstr string) error {
|
||||||
if debugstr == "" {
|
if debugstr == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, name := range strings.Split(debugstr, ",") {
|
for name := range strings.SplitSeq(debugstr, ",") {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -513,7 +513,7 @@ func lookPath(s *State, command string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pathEnv, _ := s.LookupEnv(pathEnvName())
|
pathEnv, _ := s.LookupEnv(pathEnvName())
|
||||||
for _, dir := range strings.Split(pathEnv, string(filepath.ListSeparator)) {
|
for dir := range strings.SplitSeq(pathEnv, string(filepath.ListSeparator)) {
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ func pieLinkExt(s *script.State) (bool, error) {
|
||||||
|
|
||||||
func hasGodebug(s *script.State, value string) (bool, error) {
|
func hasGodebug(s *script.State, value string) (bool, error) {
|
||||||
godebug, _ := s.LookupEnv("GODEBUG")
|
godebug, _ := s.LookupEnv("GODEBUG")
|
||||||
for _, p := range strings.Split(godebug, ",") {
|
for p := range strings.SplitSeq(godebug, ",") {
|
||||||
if strings.TrimSpace(p) == value {
|
if strings.TrimSpace(p) == value {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1123,7 +1123,7 @@ func generator(file *File) (string, bool) {
|
||||||
// opt: check Contains first to avoid unnecessary array allocation in Split.
|
// opt: check Contains first to avoid unnecessary array allocation in Split.
|
||||||
const prefix = "// Code generated "
|
const prefix = "// Code generated "
|
||||||
if strings.Contains(comment.Text, prefix) {
|
if strings.Contains(comment.Text, prefix) {
|
||||||
for _, line := range strings.Split(comment.Text, "\n") {
|
for line := range strings.SplitSeq(comment.Text, "\n") {
|
||||||
if rest, ok := strings.CutPrefix(line, prefix); ok {
|
if rest, ok := strings.CutPrefix(line, prefix); ok {
|
||||||
if gen, ok := strings.CutSuffix(rest, " DO NOT EDIT."); ok {
|
if gen, ok := strings.CutSuffix(rest, " DO NOT EDIT."); ok {
|
||||||
return gen, true
|
return gen, true
|
||||||
|
|
|
||||||
|
|
@ -1708,7 +1708,7 @@ Lines:
|
||||||
// that affect the way cgo's C code is built.
|
// that affect the way cgo's C code is built.
|
||||||
func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
|
func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
|
||||||
text := cg.Text()
|
text := cg.Text()
|
||||||
for _, line := range strings.Split(text, "\n") {
|
for line := range strings.SplitSeq(text, "\n") {
|
||||||
orig := line
|
orig := line
|
||||||
|
|
||||||
// Line is
|
// Line is
|
||||||
|
|
|
||||||
|
|
@ -406,9 +406,9 @@ func parsePlusBuildExpr(text string) (Expr, error) {
|
||||||
size := 0
|
size := 0
|
||||||
|
|
||||||
var x Expr
|
var x Expr
|
||||||
for _, clause := range strings.Fields(text) {
|
for clause := range strings.FieldsSeq(text) {
|
||||||
var y Expr
|
var y Expr
|
||||||
for _, lit := range strings.Split(clause, ",") {
|
for lit := range strings.SplitSeq(clause, ",") {
|
||||||
var z Expr
|
var z Expr
|
||||||
var neg bool
|
var neg bool
|
||||||
if strings.HasPrefix(lit, "!!") || lit == "!" {
|
if strings.HasPrefix(lit, "!!") || lit == "!" {
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ func (f gowasmFeatures) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func gowasm() (f gowasmFeatures) {
|
func gowasm() (f gowasmFeatures) {
|
||||||
for _, opt := range strings.Split(envOr("GOWASM", ""), ",") {
|
for opt := range strings.SplitSeq(envOr("GOWASM", ""), ",") {
|
||||||
switch opt {
|
switch opt {
|
||||||
case "satconv":
|
case "satconv":
|
||||||
f.SatConv = true
|
f.SatConv = true
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse names.
|
// Parse names.
|
||||||
for _, f := range strings.Split(goexp, ",") {
|
for f := range strings.SplitSeq(goexp, ",") {
|
||||||
if f == "" {
|
if f == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -489,7 +489,7 @@ func WriteImportcfg(t testing.TB, dstPath string, packageFiles map[string]string
|
||||||
t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
|
t.Fatalf("%v: %v\n%s", cmd, err, cmd.Stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
for line := range strings.SplitSeq(string(out), "\n") {
|
||||||
if line == "" {
|
if line == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ var utilFlagNames = map[string]trace.UtilFlags{
|
||||||
|
|
||||||
func requestUtilFlags(r *http.Request) trace.UtilFlags {
|
func requestUtilFlags(r *http.Request) trace.UtilFlags {
|
||||||
var flags trace.UtilFlags
|
var flags trace.UtilFlags
|
||||||
for _, flagStr := range strings.Split(r.FormValue("flags"), "|") {
|
for flagStr := range strings.SplitSeq(r.FormValue("flags"), "|") {
|
||||||
flags |= utilFlagNames[flagStr]
|
flags |= utilFlagNames[flagStr]
|
||||||
}
|
}
|
||||||
return flags
|
return flags
|
||||||
|
|
@ -119,7 +119,7 @@ func (m *mmu) HandlePlot(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var quantiles []float64
|
var quantiles []float64
|
||||||
for _, flagStr := range strings.Split(r.FormValue("flags"), "|") {
|
for flagStr := range strings.SplitSeq(r.FormValue("flags"), "|") {
|
||||||
if flagStr == "mut" {
|
if flagStr == "mut" {
|
||||||
quantiles = []float64{0, 1 - .999, 1 - .99, 1 - .95}
|
quantiles = []float64{0, 1 - .999, 1 - .99, 1 - .95}
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ func pathExt() []string {
|
||||||
var exts []string
|
var exts []string
|
||||||
x := os.Getenv(`PATHEXT`)
|
x := os.Getenv(`PATHEXT`)
|
||||||
if x != "" {
|
if x != "" {
|
||||||
for _, e := range strings.Split(strings.ToLower(x), `;`) {
|
for e := range strings.SplitSeq(strings.ToLower(x), `;`) {
|
||||||
if e == "" {
|
if e == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,3 +50,56 @@ func BenchmarkSplitAfterSeqMultiByteSeparator(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findKvBySplit(s string, k string) string {
|
||||||
|
for _, kv := range Split(s, ",") {
|
||||||
|
if HasPrefix(kv, k) {
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func findKvBySplitSeq(s string, k string) string {
|
||||||
|
for kv := range SplitSeq(s, ",") {
|
||||||
|
if HasPrefix(kv, k) {
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSplitAndSplitSeq(b *testing.B) {
|
||||||
|
testSplitString := "k1=v1,k2=v2,k3=v3,k4=v4"
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Key found",
|
||||||
|
input: "k3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Key not found",
|
||||||
|
input: "k100",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
b.Run("bySplit "+testCase.name, func(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for b.Loop() {
|
||||||
|
findKvBySplit(testSplitString, testCase.input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
b.Run("bySplitSeq "+testCase.name, func(b *testing.B) {
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for b.Loop() {
|
||||||
|
findKvBySplitSeq(testSplitString, testCase.input)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue