]> Cypherpunks repositories - gostls13.git/commitdiff
all: replace strings.Split with strings.SplitSeq
author1911860538 <alxps1911@gmail.com>
Mon, 15 Sep 2025 14:39:58 +0000 (14:39 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 16 Sep 2025 00:34:45 +0000 (17:34 -0700)
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: 392b315e122f2c9ef8703ca2dbce8f82ec198556
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>

39 files changed:
misc/ios/go_ios_exec.go
src/cmd/cgo/gcc.go
src/cmd/cgo/internal/test/testx.go
src/cmd/compile/internal/base/flag.go
src/cmd/dist/build.go
src/cmd/distpack/pack.go
src/cmd/fix/main.go
src/cmd/go/internal/auth/gitauth.go
src/cmd/go/internal/auth/gitauth_test.go
src/cmd/go/internal/auth/netrc.go
src/cmd/go/internal/doc/dirs.go
src/cmd/go/internal/doc/pkg.go
src/cmd/go/internal/list/list.go
src/cmd/go/internal/modfetch/codehost/git.go
src/cmd/go/internal/modfetch/codehost/vcs.go
src/cmd/go/internal/modindex/build.go
src/cmd/go/internal/modindex/scan.go
src/cmd/go/internal/modload/buildlist.go
src/cmd/go/internal/modload/init.go
src/cmd/go/internal/modload/vendor.go
src/cmd/go/internal/test/testflag.go
src/cmd/go/internal/toolchain/path_windows.go
src/cmd/go/internal/vcs/vcs.go
src/cmd/go/internal/vcweb/vcweb.go
src/cmd/go/internal/work/build.go
src/cmd/go/internal/work/exec.go
src/cmd/go/internal/work/gccgo.go
src/cmd/internal/objabi/flag.go
src/cmd/internal/script/cmds.go
src/cmd/internal/script/scripttest/conditions.go
src/go/ast/ast.go
src/go/build/build.go
src/go/build/constraint/expr.go
src/internal/buildcfg/cfg.go
src/internal/buildcfg/exp.go
src/internal/testenv/testenv.go
src/internal/trace/traceviewer/mmu.go
src/os/exec/lp_windows.go
src/strings/iter_test.go

index b21ce1aaf3f0cc38eb030f78dacd142c5e9b8b64..e58457d0519948377236517e231bccba438437bd 100644 (file)
@@ -212,7 +212,7 @@ func copyLocalData(dstbase string) (pkgpath string, err error) {
        // Copy all immediate files and testdata directories between
        // the package being tested and the source root.
        pkgpath = ""
-       for _, element := range strings.Split(finalPkgpath, string(filepath.Separator)) {
+       for element := range strings.SplitSeq(finalPkgpath, string(filepath.Separator)) {
                if debug {
                        log.Printf("copying %s", pkgpath)
                }
index 1f186574007b493ffbab86371705729ba2fab5cb..6c1695bdb0a6133400d719c44b6cbfd4e04ba3ed 100644 (file)
@@ -251,7 +251,7 @@ func (f *File) loadDefines(gccOptions []string) bool {
        stdout := gccDefines(b.Bytes(), gccOptions)
 
        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" {
                        continue
                }
@@ -428,7 +428,7 @@ func (p *Package) guessKinds(f *File) []*Name {
                notDeclared
        )
        sawUnmatchedErrors := false
-       for _, line := range strings.Split(stderr, "\n") {
+       for line := range strings.SplitSeq(stderr, "\n") {
                // Ignore warnings and random comments, with one
                // exception: newer GCC versions will sometimes emit
                // an error on a macro #define with a note referring
index 0e2a51a52280ba3a43f2e80e167f1445cf6f39e8..9a63b9e10087ad3f3a986130b0ec2114d6216398 100644 (file)
@@ -447,7 +447,7 @@ func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
        runtime.GC()
        buf := make([]byte, 65536)
        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") {
                        trace := strings.Split(goroutine, "\n")
                        // look for the expected function in the stack
index 1b52ab660c1c3e053e1d04ae3c3b7a68dc366df6..a0ed876cfc8e0e1d1265d5659b12612ae440e1a3 100644 (file)
@@ -570,7 +570,7 @@ func readEmbedCfg(file string) {
 
 // parseSpectre parses the spectre configuration from the string s.
 func parseSpectre(s string) {
-       for _, f := range strings.Split(s, ",") {
+       for f := range strings.SplitSeq(s, ",") {
                f = strings.TrimSpace(f)
                switch f {
                default:
index fb70047dd0e81cd5dd5ee8d22ca1e0bde271b06b..9a7951726f6f04f395a2689610ca0fe8188b5ec9 100644 (file)
@@ -380,7 +380,7 @@ func findgoversion() string {
                if i := strings.Index(b, "\n"); i >= 0 {
                        rest := b[i+1:]
                        b = chomp(b[:i])
-                       for _, line := range strings.Split(rest, "\n") {
+                       for line := range strings.SplitSeq(rest, "\n") {
                                f := strings.Fields(line)
                                if len(f) == 0 {
                                        continue
@@ -1137,7 +1137,7 @@ func shouldbuild(file, pkg string) bool {
        }
 
        // 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)
                if p == "" {
                        continue
@@ -2016,7 +2016,7 @@ func cmdlist() {
 }
 
 func setNoOpt() {
-       for _, gcflag := range strings.Split(gogcflags, " ") {
+       for gcflag := range strings.SplitSeq(gogcflags, " ") {
                if gcflag == "-N" || gcflag == "-l" {
                        noOpt = true
                        break
index 27f73e593cf6fbf65164981bf1f9edd693296a52..6bab45f1d3d642839f0ee7c212ada2a7a1566fb8 100644 (file)
@@ -271,7 +271,7 @@ func readVERSION(goroot string) (version string, t time.Time) {
                log.Fatal(err)
        }
        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)
                if len(f) == 0 {
                        continue
index 44ce396e372198c21cef239c327641f40014eb24..933c32bcd92639a1efe116130b83b7b34ce3cfd1 100644 (file)
@@ -84,14 +84,14 @@ func main() {
 
        if *allowedRewrites != "" {
                allowed = make(map[string]bool)
-               for _, f := range strings.Split(*allowedRewrites, ",") {
+               for f := range strings.SplitSeq(*allowedRewrites, ",") {
                        allowed[f] = true
                }
        }
 
        if *forceRewrites != "" {
                force = make(map[string]bool)
-               for _, f := range strings.Split(*forceRewrites, ",") {
+               for f := range strings.SplitSeq(*forceRewrites, ",") {
                        force[f] = true
                }
        }
index 29d2852814b4508d630b5b169c780eaa3d817a65..f11cd2fbf057d728569b092d472b03d1d9eb8db4 100644 (file)
@@ -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.
 func parseGitAuth(data []byte) (parsedPrefix, username, password string) {
        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), "=")
                if !ok {
                        continue
index 1ddd48fa7a889a17b879ab17b2de85168b836d34..2ba93ad2c25f17907d8817fe5be778c59cfd586c 100644 (file)
@@ -5,6 +5,7 @@
 package auth
 
 import (
+       "strings"
        "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
+                       }
+               })
+       }
+}
index 4191ccb29304d6ad9897f0936195f269dbca50ce..78c884b31bd54101a5fbb60444c2060483b61c5a 100644 (file)
@@ -24,7 +24,7 @@ func parseNetrc(data string) []netrcLine {
        var nrc []netrcLine
        var l netrcLine
        inMacro := false
-       for _, line := range strings.Split(data, "\n") {
+       for line := range strings.SplitSeq(data, "\n") {
                if inMacro {
                        if line == "" {
                                inMacro = false
index 8b1670f61c253c5e0ef5795878c0ecee2f797884..5efd40b1d5a48bb6a710ee26bca350169463e27b 100644 (file)
@@ -241,7 +241,7 @@ func findCodeRoots() []Dir {
        cmd := exec.Command(goCmd(), "list", "-m", "-f={{.Path}}\t{{.Dir}}", "all")
        cmd.Stderr = os.Stderr
        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")
                if dir != "" {
                        list = append(list, Dir{importPath: path, dir: dir, inModule: true})
index 953b0d9a2840ab603a8fbe8d0c12ccc39a078639..7b5e00365d04a12cd12de493fea5f270d1af0b25 100644 (file)
@@ -920,7 +920,7 @@ func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldLis
 
                        start := doc.List[0].Slash
                        doc.List = doc.List[:0]
-                       for _, line := range strings.Split(text, "\n") {
+                       for line := range strings.SplitSeq(text, "\n") {
                                prefix := "// "
                                if len(line) > 0 && line[0] == '\t' {
                                        prefix = "//"
index 86a6b1792c5016a84d5a95525daf27cbfb2448fc..bee7dc8053ee6f8b140d7ddef869863d7575f49f 100644 (file)
@@ -381,7 +381,7 @@ func (v *jsonFlag) Set(s string) error {
        if *v == nil {
                *v = make(map[string]bool)
        }
-       for _, f := range strings.Split(s, ",") {
+       for f := range strings.SplitSeq(s, ",") {
                (*v)[f] = true
        }
        return nil
index 8a1c12b90a5c07309ca2d3c0bad0ccb12ee27aa3..74c4c646cdc5824fca791c0efd5b4951c4afbf87 100644 (file)
@@ -173,7 +173,7 @@ func (r *gitRepo) loadLocalTags(ctx context.Context) {
                return
        }
 
-       for _, line := range strings.Split(string(out), "\n") {
+       for line := range strings.SplitSeq(string(out), "\n") {
                if line != "" {
                        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)
-               for _, line := range strings.Split(string(out), "\n") {
+               for line := range strings.SplitSeq(string(out), "\n") {
                        f := strings.Fields(line)
                        if len(f) != 2 {
                                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
                var highest string
-               for _, line := range strings.Split(string(out), "\n") {
+               for line := range strings.SplitSeq(string(out), "\n") {
                        line = strings.TrimSpace(line)
                        // 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.
index 8e59479339be7f13a904a3886eb1117d0e129c9b..d80397502b422d0ec138bc23de45c0d29203e09f 100644 (file)
@@ -561,7 +561,7 @@ func bzrParseStat(rev, out string) (*RevInfo, error) {
        var revno int64
        var tm time.Time
        var tags []string
-       for _, line := range strings.Split(out, "\n") {
+       for line := range strings.SplitSeq(out, "\n") {
                if line == "" || line[0] == ' ' || line[0] == '\t' {
                        // End of header, start of commit message.
                        break
@@ -614,7 +614,7 @@ func bzrParseStat(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:") {
                        f := strings.Fields(line)
                        if len(f) != 5 || len(f[1]) != 40 || f[4] != "UTC" {
index 761bda8d39b158dad9fee22b188b9ed47f327a5c..0fa78afe2c08ef92b0ad006e8c539ffc656209e0 100644 (file)
@@ -426,7 +426,7 @@ Lines:
 // These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
 // that affect the way cgo's C code is built.
 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
 
                // Line is
index 90be154e8ea69ac789b7f4c6b99e3148096002ba..af2c0abe046d7203fc73ec7056223240af93dc69 100644 (file)
@@ -275,7 +275,7 @@ func importRaw(modroot, reldir string) *rawPackage {
 // which is the comment on import "C".
 func extractCgoDirectives(doc string) []string {
        var out []string
-       for _, line := range strings.Split(doc, "\n") {
+       for line := range strings.SplitSeq(doc, "\n") {
                // Line is
                //      #cgo [GOOS/GOARCH...] LDFLAGS: stuff
                //
index cd3ec4f102473b26da273ce1c372e249601e966d..2ba04f707b54722b2db098f39a59a3ce2145521d 100644 (file)
@@ -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
                // normal operation all the time).
                readModGraphDebugOnce.Do(func() {
-                       for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") {
+                       for f := range strings.SplitSeq(os.Getenv("GODEBUG"), ",") {
                                switch f {
                                case "lazymod=log":
                                        debug.PrintStack()
index 25151103edb5796bc1c30219b3655243f0758aab..498ff7433ea6feaf0cd2ab61e68c0f09e23a7ebc 100644 (file)
@@ -1597,7 +1597,7 @@ func modulesTextIsForWorkspace(vendorDir string) (bool, error) {
        }
        line, _, _ := strings.Cut(string(buf[:n]), "\n")
        if annotations, ok := strings.CutPrefix(line, "## "); ok {
-               for _, entry := range strings.Split(annotations, ";") {
+               for entry := range strings.SplitSeq(annotations, ";") {
                        entry = strings.TrimSpace(entry)
                        if entry == "workspace" {
                                return true, nil
index b2cb44100ec4d28e24f711d06b7578287fcba06f..c7fe73193582abd9cf8000831e772c9635793100 100644 (file)
@@ -53,7 +53,7 @@ func readVendorList(vendorDir string) {
                }
 
                var mod module.Version
-               for _, line := range strings.Split(string(data), "\n") {
+               for line := range strings.SplitSeq(string(data), "\n") {
                        if strings.HasPrefix(line, "# ") {
                                f := strings.Fields(line)
 
@@ -103,7 +103,7 @@ func readVendorList(vendorDir string) {
                        if annotations, ok := strings.CutPrefix(line, "## "); ok {
                                // Metadata. Take the union of annotations across multiple lines, if present.
                                meta := vendorMeta[mod]
-                               for _, entry := range strings.Split(annotations, ";") {
+                               for entry := range strings.SplitSeq(annotations, ";") {
                                        entry = strings.TrimSpace(entry)
                                        if entry == "explicit" {
                                                meta.Explicit = true
index 09e41533b6ccbabd41a0148bb4db3bc4a044d096..983e8f56e9af096c2d2af1da168a68bde1fd0221 100644 (file)
@@ -149,7 +149,7 @@ func (f *vetFlag) Set(value string) error {
 
        *f = vetFlag{explicit: true}
        var single string
-       for _, arg := range strings.Split(value, ",") {
+       for arg := range strings.SplitSeq(value, ",") {
                switch arg {
                case "":
                        return fmt.Errorf("-vet argument contains empty list element")
index d88945ddc8ecea06ce7be12d04adb2a9e1f7ca9f..dfb2238a4dbb97333547e60c9142382a0fd5aa43 100644 (file)
@@ -21,7 +21,7 @@ var pathExts = sync.OnceValue(func() []string {
        }
 
        var exts []string
-       for _, e := range strings.Split(strings.ToLower(x), `;`) {
+       for e := range strings.SplitSeq(strings.ToLower(x), `;`) {
                if e == "" {
                        continue
                }
index 7e081eb41a1c4edd67493c9231ca684af5f24dff..edbc5734401737be258ebc72cd238b1f97f883b5 100644 (file)
@@ -110,7 +110,7 @@ func (v *Cmd) isSecureScheme(scheme string) bool {
                // colon-separated list of schemes that are allowed to be used with git
                // fetch/clone. Any scheme not mentioned will be considered insecure.
                if allow := os.Getenv("GIT_ALLOW_PROTOCOL"); allow != "" {
-                       for _, s := range strings.Split(allow, ":") {
+                       for s := range strings.SplitSeq(allow, ":") {
                                if s == scheme {
                                        return true
                                }
@@ -440,7 +440,7 @@ func bzrStatus(vcsBzr *Cmd, rootDir string) (Status, error) {
        var rev string
        var commitTime time.Time
 
-       for _, line := range strings.Split(out, "\n") {
+       for line := range strings.SplitSeq(out, "\n") {
                i := strings.IndexByte(line, ':')
                if i < 0 {
                        continue
@@ -974,7 +974,7 @@ func parseGOVCS(s string) (govcsConfig, error) {
        }
        var cfg govcsConfig
        have := make(map[string]string)
-       for _, item := range strings.Split(s, ",") {
+       for item := range strings.SplitSeq(s, ",") {
                item = strings.TrimSpace(item)
                if item == "" {
                        return nil, fmt.Errorf("empty entry in GOVCS")
index 757a595808c7b774894d3f61579d55c0eb0e7d5a..b81ff5e63de72aeb1e20a71e38a1b37a6f1e4f4e 100644 (file)
@@ -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
        // ".txt" script (because the search would ignore that file).
        scriptPath := "."
-       for _, part := range strings.Split(clean, "/") {
+       for part := range strings.SplitSeq(clean, "/") {
                scriptPath = filepath.Join(scriptPath, part)
                dir := filepath.Join(s.scriptDir, scriptPath)
                if _, err := os.Stat(dir); err != nil {
index 742efe6490f34770a15414ffd3f63f65c60c36a7..6741b39f051cd6d1165faf6f2d2e1a5e3edd568a 100644 (file)
@@ -384,7 +384,7 @@ func (v *tagsFlag) Set(s string) error {
 
        // Split on commas, ignore empty strings.
        *v = []string{}
-       for _, s := range strings.Split(s, ",") {
+       for s := range strings.SplitSeq(s, ",") {
                if s != "" {
                        *v = append(*v, s)
                }
index 51cb2e5a0411df14fa763ba7417c3edb9e06c2ef..72b9177c9dbbeb86b55eb8e9bc3ec3f6be5e76c2 100644 (file)
@@ -1122,7 +1122,7 @@ func (b *Builder) loadCachedVet(a *Action) error {
                return fmt.Errorf("reading srcfiles list: %w", err)
        }
        var srcfiles []string
-       for _, name := range strings.Split(string(list), "\n") {
+       for name := range strings.SplitSeq(string(list), "\n") {
                if name == "" { // end of list
                        continue
                }
@@ -1146,7 +1146,7 @@ func (b *Builder) loadCachedCompiledGoFiles(a *Action) error {
                return fmt.Errorf("reading srcfiles list: %w", err)
        }
        var gofiles []string
-       for _, name := range strings.Split(string(list), "\n") {
+       for name := range strings.SplitSeq(string(list), "\n") {
                if name == "" { // end of list
                        continue
                } else if !strings.HasSuffix(name, ".go") {
index b42e92ea690981a2bbbd2e90bdb9a04455275dd4..276e082b71ba55c209a386dfbe8a3704ab471452 100644 (file)
@@ -278,7 +278,7 @@ func (tools gccgoToolchain) link(b *Builder, root *Action, out, importcfg string
                        return err
                }
                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) {
                                flag := line[len(ldflagsPrefix):]
                                // Every _cgo_flags file has -g and -O2 in _CGO_LDFLAGS
index 1bb46e3bcdc01b642e2341662966ddc5021d294f..8709c4e5cf530e141a799f206f85256d07e1e85b 100644 (file)
@@ -277,7 +277,7 @@ func (f *DebugFlag) Set(debugstr string) error {
        if debugstr == "" {
                return nil
        }
-       for _, name := range strings.Split(debugstr, ",") {
+       for name := range strings.SplitSeq(debugstr, ",") {
                if name == "" {
                        continue
                }
index 7a930caf355d666f66bac35c0856a94aee293a6d..a682d9b3f66863a2a23a3e0e2ac9bbb30c2c4480 100644 (file)
@@ -513,7 +513,7 @@ func lookPath(s *State, command string) (string, error) {
        }
 
        pathEnv, _ := s.LookupEnv(pathEnvName())
-       for _, dir := range strings.Split(pathEnv, string(filepath.ListSeparator)) {
+       for dir := range strings.SplitSeq(pathEnv, string(filepath.ListSeparator)) {
                if dir == "" {
                        continue
                }
index e35ac2ddb7412fc748995caa90730eadc8a962f9..6702e9279b106b5fb99d6b2e7361799aff806aef 100644 (file)
@@ -88,7 +88,7 @@ func pieLinkExt(s *script.State) (bool, error) {
 
 func hasGodebug(s *script.State, value string) (bool, error) {
        godebug, _ := s.LookupEnv("GODEBUG")
-       for _, p := range strings.Split(godebug, ",") {
+       for p := range strings.SplitSeq(godebug, ",") {
                if strings.TrimSpace(p) == value {
                        return true, nil
                }
index a3dc0c3220d19c47b870b9b3753e2698653f02a3..2a0d9e686021e87202148becc9ddcd81f06cb17e 100644 (file)
@@ -1123,7 +1123,7 @@ func generator(file *File) (string, bool) {
                        // opt: check Contains first to avoid unnecessary array allocation in Split.
                        const prefix = "// Code generated "
                        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 gen, ok := strings.CutSuffix(rest, " DO NOT EDIT."); ok {
                                                        return gen, true
index 50288fcec64141f531920cc824592d314d8748b8..76866c7487adf43eb257eaf6e7ed68c645c77ee1 100644 (file)
@@ -1708,7 +1708,7 @@ Lines:
 // that affect the way cgo's C code is built.
 func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
        text := cg.Text()
-       for _, line := range strings.Split(text, "\n") {
+       for line := range strings.SplitSeq(text, "\n") {
                orig := line
 
                // Line is
index 0f05f8db6a48cb0c629a1356b7ea527caba099e6..1f39bb20110adf733f91d02652f3de50b911ee94 100644 (file)
@@ -406,9 +406,9 @@ func parsePlusBuildExpr(text string) (Expr, error) {
        size := 0
 
        var x Expr
-       for _, clause := range strings.Fields(text) {
+       for clause := range strings.FieldsSeq(text) {
                var y Expr
-               for _, lit := range strings.Split(clause, ",") {
+               for lit := range strings.SplitSeq(clause, ",") {
                        var z Expr
                        var neg bool
                        if strings.HasPrefix(lit, "!!") || lit == "!" {
index 7e4ee365dfe126376807fafd63c97c1abeee4ba3..52d1c45afa6fb613ba5766901a5cf457727296ea 100644 (file)
@@ -336,7 +336,7 @@ func (f gowasmFeatures) String() string {
 }
 
 func gowasm() (f gowasmFeatures) {
-       for _, opt := range strings.Split(envOr("GOWASM", ""), ",") {
+       for opt := range strings.SplitSeq(envOr("GOWASM", ""), ",") {
                switch opt {
                case "satconv":
                        f.SatConv = true
index 310226bc0146e58914000e3059c3bff07809bde8..be9d1f71258cafb356646436c2007ebdbf159205 100644 (file)
@@ -113,7 +113,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
                }
 
                // Parse names.
-               for _, f := range strings.Split(goexp, ",") {
+               for f := range strings.SplitSeq(goexp, ",") {
                        if f == "" {
                                continue
                        }
index 947340ce83ea127f1428cfb9305b03496d38eba6..96eacc60a3a07d1df6479cd90407e85613c809d9 100644 (file)
@@ -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)
                }
 
-               for _, line := range strings.Split(string(out), "\n") {
+               for line := range strings.SplitSeq(string(out), "\n") {
                        if line == "" {
                                continue
                        }
index 0bc1233b44dee9717891e3342c6e3234c2ef12f5..190ce5afcad6e1dad688ff1e36072caffdd681fd 100644 (file)
@@ -69,7 +69,7 @@ var utilFlagNames = map[string]trace.UtilFlags{
 
 func requestUtilFlags(r *http.Request) 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]
        }
        return flags
@@ -119,7 +119,7 @@ func (m *mmu) HandlePlot(w http.ResponseWriter, r *http.Request) {
        }
 
        var quantiles []float64
-       for _, flagStr := range strings.Split(r.FormValue("flags"), "|") {
+       for flagStr := range strings.SplitSeq(r.FormValue("flags"), "|") {
                if flagStr == "mut" {
                        quantiles = []float64{0, 1 - .999, 1 - .99, 1 - .95}
                        break
index e01e7bbbbabde08eb9b4dbeae613dd40727d966a..74537dec687c81e5d4b1c9222e51447535af2c7d 100644 (file)
@@ -123,7 +123,7 @@ func pathExt() []string {
        var exts []string
        x := os.Getenv(`PATHEXT`)
        if x != "" {
-               for _, e := range strings.Split(strings.ToLower(x), `;`) {
+               for e := range strings.SplitSeq(strings.ToLower(x), `;`) {
                        if e == "" {
                                continue
                        }
index 2db599377f0fc03b3a2b7cb6cb9964ff07e83a7b..963349b1f414410b27011c3307f94c8269561513 100644 (file)
@@ -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)
+                       }
+               })
+       }
+}