diff options
| author | Damien Neil <dneil@google.com> | 2024-04-24 09:52:52 -0700 |
|---|---|---|
| committer | Damien Neil <dneil@google.com> | 2024-04-26 23:07:25 +0000 |
| commit | d69f87445cb28d68b4d62d8b80dff8a8d5a9203e (patch) | |
| tree | b5f23075804f9042f28a2b11d5c74a1d4d46bfff /src/internal/stringslite | |
| parent | 1c4704991aeae5c9e0c6816a227390829b724ab5 (diff) | |
| download | go-d69f87445cb28d68b4d62d8b80dff8a8d5a9203e.tar.xz | |
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>
Diffstat (limited to 'src/internal/stringslite')
| -rw-r--r-- | src/internal/stringslite/strings.go | 124 |
1 files changed, 124 insertions, 0 deletions
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 +} |
