mirror of
https://github.com/golang/go.git
synced 2025-05-20 23:03:26 +00:00
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>
This commit is contained in:
parent
1c4704991a
commit
d69f87445c
@ -66,6 +66,7 @@ var depsRules = `
|
|||||||
internal/goexperiment,
|
internal/goexperiment,
|
||||||
internal/goos
|
internal/goos
|
||||||
< internal/bytealg
|
< internal/bytealg
|
||||||
|
< internal/stringslite
|
||||||
< internal/itoa
|
< internal/itoa
|
||||||
< internal/unsafeheader
|
< internal/unsafeheader
|
||||||
< runtime/internal/sys
|
< runtime/internal/sys
|
||||||
|
124
src/internal/stringslite/strings.go
Normal file
124
src/internal/stringslite/strings.go
Normal file
@ -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
|
||||||
|
}
|
@ -9,6 +9,7 @@ package strings
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"internal/bytealg"
|
"internal/bytealg"
|
||||||
|
"internal/stringslite"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"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.
|
// 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 {
|
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
|
// 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.
|
// HasPrefix reports whether the string s begins with prefix.
|
||||||
func HasPrefix(s, prefix string) bool {
|
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.
|
// HasSuffix reports whether the string s ends with suffix.
|
||||||
func HasSuffix(s, suffix string) bool {
|
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
|
// 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.
|
// 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 {
|
func Index(s, substr string) int {
|
||||||
n := len(substr)
|
return stringslite.Index(s, 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cut slices s around the first instance of sep,
|
// 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.
|
// The found result reports whether sep appears in s.
|
||||||
// If sep does not appear in s, cut returns s, "", false.
|
// If sep does not appear in s, cut returns s, "", false.
|
||||||
func Cut(s, sep string) (before, after string, found bool) {
|
func Cut(s, sep string) (before, after string, found bool) {
|
||||||
if i := Index(s, sep); i >= 0 {
|
return stringslite.Cut(s, sep)
|
||||||
return s[:i], s[i+len(sep):], true
|
|
||||||
}
|
|
||||||
return s, "", false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CutPrefix returns s without the provided leading prefix string
|
// 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 s doesn't start with prefix, CutPrefix returns s, false.
|
||||||
// If prefix is the empty string, CutPrefix returns s, true.
|
// If prefix is the empty string, CutPrefix returns s, true.
|
||||||
func CutPrefix(s, prefix string) (after string, found bool) {
|
func CutPrefix(s, prefix string) (after string, found bool) {
|
||||||
if !HasPrefix(s, prefix) {
|
return stringslite.CutPrefix(s, prefix)
|
||||||
return s, false
|
|
||||||
}
|
|
||||||
return s[len(prefix):], true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CutSuffix returns s without the provided ending suffix string
|
// 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 s doesn't end with suffix, CutSuffix returns s, false.
|
||||||
// If suffix is the empty string, CutSuffix returns s, true.
|
// If suffix is the empty string, CutSuffix returns s, true.
|
||||||
func CutSuffix(s, suffix string) (before string, found bool) {
|
func CutSuffix(s, suffix string) (before string, found bool) {
|
||||||
if !HasSuffix(s, suffix) {
|
return stringslite.CutSuffix(s, suffix)
|
||||||
return s, false
|
|
||||||
}
|
|
||||||
return s[:len(s)-len(suffix)], true
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user