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