| // 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". | |
| // | |
| // Most tests for these functions are in the strings package. | |
| package stringslite | |
| import ( | |
| "internal/bytealg" | |
| "unsafe" | |
| ) | |
| func HasPrefix(s, prefix string) bool { | |
| return len(s) >= len(prefix) && s[: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 || substr == s: | |
| return 0 | |
| case n == 1: | |
| return IndexByte(s, substr[0]) | |
| case n >= len(s): | |
| return -1 | |
| case n <= bytealg.MaxLen && len(s) <= bytealg.MaxBruteForce: | |
| // Use brute force when s and substr both are small | |
| 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 | |
| } | |
| i++ | |
| fails++ | |
| if n <= bytealg.MaxLen && fails > bytealg.Cutover(i) { | |
| // Switch to bytealg.IndexString when IndexByte produces too many false positives. | |
| r := bytealg.IndexString(s[i:], substr) | |
| if r >= 0 { | |
| return r + i | |
| } | |
| return -1 | |
| } else if n > bytealg.MaxLen && 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 | |
| } | |
| func TrimPrefix(s, prefix string) string { | |
| if HasPrefix(s, prefix) { | |
| return s[len(prefix):] | |
| } | |
| return s | |
| } | |
| func TrimSuffix(s, suffix string) string { | |
| if HasSuffix(s, suffix) { | |
| return s[:len(s)-len(suffix)] | |
| } | |
| return s | |
| } | |
| // IsSpace reports whether r is a space character as defined by | |
| // Unicode's White Space property. | |
| func IsSpace(r rune) bool { | |
| switch r { | |
| case '\t', '\n', '\v', '\f', '\r', ' ', | |
| 0x0085, // NEXT LINE | |
| 0x00A0, // NO-BREAK SPACE | |
| 0x1680, // OGHAM SPACE MARK | |
| 0x2000, // EN QUAD | |
| 0x2001, // EM QUAD | |
| 0x2002, // EN SPACE | |
| 0x2003, // EM SPACE | |
| 0x2004, // THREE-PER-EM SPACE | |
| 0x2005, // FOUR-PER-EM SPACE | |
| 0x2006, // SIX-PER-EM SPACE | |
| 0x2007, // FIGURE SPACE | |
| 0x2008, // PUNCTUATION SPACE | |
| 0x2009, // THIN SPACE | |
| 0x200A, // HAIR SPACE | |
| 0x2028, // LINE SEPARATOR | |
| 0x2029, // PARAGRAPH SEPARATOR | |
| 0x202F, // NARROW NO-BREAK SPACE | |
| 0x205F, // MEDIUM MATHEMATICAL SPACE | |
| 0x3000: // IDEOGRAPHIC SPACE | |
| return true | |
| } | |
| return false | |
| } | |
| func Clone(s string) string { | |
| if len(s) == 0 { | |
| return "" | |
| } | |
| b := make([]byte, len(s)) | |
| copy(b, s) | |
| return unsafe.String(&b[0], len(b)) | |
| } |