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.
9package stringslite
10
11import (
12	"internal/bytealg"
13	"unsafe"
14)
15
16func HasPrefix(s, prefix string) bool {
17	return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
18}
19
20func HasSuffix(s, suffix string) bool {
21	return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
22}
23
24func IndexByte(s string, c byte) int {
25	return bytealg.IndexByteString(s, c)
26}
27
28func Index(s, substr string) 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
108func Cut(s, sep string) (before, after string, 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
115func CutPrefix(s, prefix string) (after string, found bool) {
116	if !HasPrefix(s, prefix) {
117		return s, false
118	}
119	return s[len(prefix):], true
120}
121
122func CutSuffix(s, suffix string) (before string, found bool) {
123	if !HasSuffix(s, suffix) {
124		return s, false
125	}
126	return s[:len(s)-len(suffix)], true
127}
128
129func TrimPrefix(s, prefix string) string {
130	if HasPrefix(s, prefix) {
131		return s[len(prefix):]
132	}
133	return s
134}
135
136func TrimSuffix(s, suffix string) string {
137	if HasSuffix(s, suffix) {
138		return s[:len(s)-len(suffix)]
139	}
140	return s
141}
142
143func Clone(s string) string {
144	if len(s) == 0 {
145		return ""
146	}
147	b := make([]byte, len(s))
148	copy(b, s)
149	return unsafe.String(&b[0], len(b))
150}
151