aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2024-04-24 09:52:52 -0700
committerDamien Neil <dneil@google.com>2024-04-26 23:07:25 +0000
commitd69f87445cb28d68b4d62d8b80dff8a8d5a9203e (patch)
treeb5f23075804f9042f28a2b11d5c74a1d4d46bfff
parent1c4704991aeae5c9e0c6816a227390829b724ab5 (diff)
downloadgo-d69f87445cb28d68b4d62d8b80dff8a8d5a9203e.tar.gz
go-d69f87445cb28d68b4d62d8b80dff8a8d5a9203e.zip
strings, internal/stringslite: lite version of strings package
To be used by internal/filepathlite, which is to be used by os. There are probably other places where it would be convenient to have strings functions accessible to RUNTIME level packages. Change-Id: Icda59e7a9e26d9e8f3692db0ea4fb7b3dbf570d7 Reviewed-on: https://go-review.googlesource.com/c/go/+/581516 Reviewed-by: Ian Lance Taylor <iant@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
-rw-r--r--src/go/build/deps_test.go1
-rw-r--r--src/internal/stringslite/strings.go124
-rw-r--r--src/strings/strings.go100
3 files changed, 133 insertions, 92 deletions
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 0ea34b1bd7..a3ba8092be 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -66,6 +66,7 @@ var depsRules = `
internal/goexperiment,
internal/goos
< internal/bytealg
+ < internal/stringslite
< internal/itoa
< internal/unsafeheader
< runtime/internal/sys
diff --git a/src/internal/stringslite/strings.go b/src/internal/stringslite/strings.go
new file mode 100644
index 0000000000..ce8a913297
--- /dev/null
+++ b/src/internal/stringslite/strings.go
@@ -0,0 +1,124 @@
+// Copyright 2024 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.
+
+// Package stringslite implements a subset of strings,
+// only using packages that may be imported by "os".
+//
+// Tests for these functions are in the strings package.
+package stringslite
+
+import "internal/bytealg"
+
+func HasPrefix(s, prefix string) bool {
+ return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
+}
+
+func HasSuffix(s, suffix string) bool {
+ return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
+}
+
+func IndexByte(s string, c byte) int {
+ return bytealg.IndexByteString(s, c)
+}
+
+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)
+ }
+ c0 := substr[0]
+ c1 := substr[1]
+ i := 0
+ t := len(s) - n + 1
+ fails := 0
+ for i < t {
+ if s[i] != c0 {
+ // IndexByte is faster than bytealg.IndexString, so use it as long as
+ // we're not getting lots of false positives.
+ o := IndexByte(s[i+1:t], c0)
+ if o < 0 {
+ return -1
+ }
+ i += o + 1
+ }
+ if s[i+1] == c1 && 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
+ }
+ c0 := substr[0]
+ c1 := substr[1]
+ i := 0
+ t := len(s) - n + 1
+ fails := 0
+ for i < t {
+ if s[i] != c0 {
+ o := IndexByte(s[i+1:t], c0)
+ if o < 0 {
+ return -1
+ }
+ i += o + 1
+ }
+ if s[i+1] == c1 && s[i:i+n] == substr {
+ return i
+ }
+ i++
+ fails++
+ if fails >= 4+i>>4 && i < t {
+ // See comment in ../bytes/bytes.go.
+ j := bytealg.IndexRabinKarp(s[i:], substr)
+ if j < 0 {
+ return -1
+ }
+ return i + j
+ }
+ }
+ return -1
+}
+
+func Cut(s, sep string) (before, after string, found bool) {
+ if i := Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, "", false
+}
+
+func CutPrefix(s, prefix string) (after string, found bool) {
+ if !HasPrefix(s, prefix) {
+ return s, false
+ }
+ return s[len(prefix):], true
+}
+
+func CutSuffix(s, suffix string) (before string, found bool) {
+ if !HasSuffix(s, suffix) {
+ return s, false
+ }
+ return s[:len(s)-len(suffix)], true
+}
diff --git a/src/strings/strings.go b/src/strings/strings.go
index f53ae1f9a7..11c558c4c3 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -9,6 +9,7 @@ package strings
import (
"internal/bytealg"
+ "internal/stringslite"
"unicode"
"unicode/utf8"
)
@@ -115,7 +116,7 @@ func LastIndex(s, substr string) int {
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
func IndexByte(s string, c byte) int {
- return bytealg.IndexByteString(s, c)
+ return stringslite.IndexByte(s, c)
}
// IndexRune returns the index of the first instance of the Unicode code point
@@ -460,12 +461,12 @@ func Join(elems []string, sep string) string {
// HasPrefix reports whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {
- return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
+ return stringslite.HasPrefix(s, prefix)
}
// HasSuffix reports whether the string s ends with suffix.
func HasSuffix(s, suffix string) bool {
- return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
+ return stringslite.HasSuffix(s, suffix)
}
// Map returns a copy of the string s with all its characters modified
@@ -1225,83 +1226,7 @@ hasUnicode:
// 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)
- }
- c0 := substr[0]
- c1 := substr[1]
- i := 0
- t := len(s) - n + 1
- fails := 0
- for i < t {
- if s[i] != c0 {
- // IndexByte is faster than bytealg.IndexString, so use it as long as
- // we're not getting lots of false positives.
- o := IndexByte(s[i+1:t], c0)
- if o < 0 {
- return -1
- }
- i += o + 1
- }
- if s[i+1] == c1 && 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
- }
- c0 := substr[0]
- c1 := substr[1]
- i := 0
- t := len(s) - n + 1
- fails := 0
- for i < t {
- if s[i] != c0 {
- o := IndexByte(s[i+1:t], c0)
- if o < 0 {
- return -1
- }
- i += o + 1
- }
- if s[i+1] == c1 && s[i:i+n] == substr {
- return i
- }
- i++
- fails++
- if fails >= 4+i>>4 && i < t {
- // See comment in ../bytes/bytes.go.
- j := bytealg.IndexRabinKarp(s[i:], substr)
- if j < 0 {
- return -1
- }
- return i + j
- }
- }
- return -1
+ return stringslite.Index(s, substr)
}
// Cut slices s around the first instance of sep,
@@ -1309,10 +1234,7 @@ func Index(s, substr string) int {
// The found result reports whether sep appears in s.
// If sep does not appear in s, cut returns s, "", false.
func Cut(s, sep string) (before, after string, found bool) {
- if i := Index(s, sep); i >= 0 {
- return s[:i], s[i+len(sep):], true
- }
- return s, "", false
+ return stringslite.Cut(s, sep)
}
// CutPrefix returns s without the provided leading prefix string
@@ -1320,10 +1242,7 @@ func Cut(s, sep string) (before, after string, found bool) {
// If s doesn't start with prefix, CutPrefix returns s, false.
// If prefix is the empty string, CutPrefix returns s, true.
func CutPrefix(s, prefix string) (after string, found bool) {
- if !HasPrefix(s, prefix) {
- return s, false
- }
- return s[len(prefix):], true
+ return stringslite.CutPrefix(s, prefix)
}
// CutSuffix returns s without the provided ending suffix string
@@ -1331,8 +1250,5 @@ func CutPrefix(s, prefix string) (after string, found bool) {
// If s doesn't end with suffix, CutSuffix returns s, false.
// If suffix is the empty string, CutSuffix returns s, true.
func CutSuffix(s, suffix string) (before string, found bool) {
- if !HasSuffix(s, suffix) {
- return s, false
- }
- return s[:len(s)-len(suffix)], true
+ return stringslite.CutSuffix(s, suffix)
}