strings.mx raw
1 // Copyright 2024 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package stringslite implements a subset of strings,
6 // only using packages that may be imported by "os".
7 //
8 // Tests for these functions are in the strings package.
9 package stringslite
10
11 import (
12 "internal/bytealg"
13 "unsafe"
14 )
15
16 func HasPrefix(s, prefix []byte) bool {
17 return len(s) >= len(prefix) && s[:len(prefix)] == prefix
18 }
19
20 func HasSuffix(s, suffix []byte) bool {
21 return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
22 }
23
24 func IndexByte(s []byte, c byte) int {
25 return bytealg.IndexByteString(s, c)
26 }
27
28 func Index(s, substr []byte) int {
29 n := len(substr)
30 switch {
31 case n == 0:
32 return 0
33 case n == 1:
34 return IndexByte(s, substr[0])
35 case n == len(s):
36 if substr == s {
37 return 0
38 }
39 return -1
40 case n > len(s):
41 return -1
42 case n <= bytealg.MaxLen:
43 // Use brute force when s and substr both are small
44 if len(s) <= bytealg.MaxBruteForce {
45 return bytealg.IndexString(s, substr)
46 }
47 c0 := substr[0]
48 c1 := substr[1]
49 i := 0
50 t := len(s) - n + 1
51 fails := 0
52 for i < t {
53 if s[i] != c0 {
54 // IndexByte is faster than bytealg.IndexString, so use it as long as
55 // we're not getting lots of false positives.
56 o := IndexByte(s[i+1:t], c0)
57 if o < 0 {
58 return -1
59 }
60 i += o + 1
61 }
62 if s[i+1] == c1 && s[i:i+n] == substr {
63 return i
64 }
65 fails++
66 i++
67 // Switch to bytealg.IndexString when IndexByte produces too many false positives.
68 if fails > bytealg.Cutover(i) {
69 r := bytealg.IndexString(s[i:], substr)
70 if r >= 0 {
71 return r + i
72 }
73 return -1
74 }
75 }
76 return -1
77 }
78 c0 := substr[0]
79 c1 := substr[1]
80 i := 0
81 t := len(s) - n + 1
82 fails := 0
83 for i < t {
84 if s[i] != c0 {
85 o := IndexByte(s[i+1:t], c0)
86 if o < 0 {
87 return -1
88 }
89 i += o + 1
90 }
91 if s[i+1] == c1 && s[i:i+n] == substr {
92 return i
93 }
94 i++
95 fails++
96 if fails >= 4+i>>4 && i < t {
97 // See comment in ../bytes/bytes.go.
98 j := bytealg.IndexRabinKarp(s[i:], substr)
99 if j < 0 {
100 return -1
101 }
102 return i + j
103 }
104 }
105 return -1
106 }
107
108 func Cut(s, sep []byte) (before, after []byte, found bool) {
109 if i := Index(s, sep); i >= 0 {
110 return s[:i], s[i+len(sep):], true
111 }
112 return s, "", false
113 }
114
115 func CutPrefix(s, prefix []byte) (after []byte, found bool) {
116 if !HasPrefix(s, prefix) {
117 return s, false
118 }
119 return s[len(prefix):], true
120 }
121
122 func CutSuffix(s, suffix []byte) (before []byte, found bool) {
123 if !HasSuffix(s, suffix) {
124 return s, false
125 }
126 return s[:len(s)-len(suffix)], true
127 }
128
129 func TrimPrefix(s, prefix []byte) []byte {
130 if HasPrefix(s, prefix) {
131 return s[len(prefix):]
132 }
133 return s
134 }
135
136 func TrimSuffix(s, suffix []byte) []byte {
137 if HasSuffix(s, suffix) {
138 return s[:len(s)-len(suffix)]
139 }
140 return s
141 }
142
143 func Clone(s []byte) []byte {
144 if len(s) == 0 {
145 return ""
146 }
147 b := []byte{:len(s)}
148 copy(b, s)
149 return unsafe.String(&b[0], len(b))
150 }
151