github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/utils/strutil/strutil.go (about) 1 package strutil 2 3 import ( 4 "unicode" 5 "unicode/utf8" 6 ) 7 8 // equalFoldRune compares a and b runes whether they fold equally. 9 // 10 // The code comes from strings.EqualFold, but shortened to only one rune. 11 func equalFoldRune(sr, tr rune) bool { 12 if sr == tr { 13 return true 14 } 15 // Make sr < tr to simplify what follows. 16 if tr < sr { 17 sr, tr = tr, sr 18 } 19 // Fast check for ASCII. 20 if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' { 21 // ASCII, and sr is upper case. tr must be lower case. 22 if tr == sr+'a'-'A' { 23 return true 24 } 25 return false 26 } 27 28 // General case. SimpleFold(x) returns the next equivalent rune > x 29 // or wraps around to smaller values. 30 r := unicode.SimpleFold(sr) 31 for r != sr && r < tr { 32 r = unicode.SimpleFold(r) 33 } 34 if r == tr { 35 return true 36 } 37 return false 38 } 39 40 // HasPrefixFold is like strings.HasPrefix but uses Unicode case-folding, 41 // matching case insensitively. 42 func HasPrefixFold(s, prefix string) bool { 43 if prefix == "" { 44 return true 45 } 46 for _, pr := range prefix { 47 if s == "" { 48 return false 49 } 50 // step with s, too 51 sr, size := utf8.DecodeRuneInString(s) 52 if sr == utf8.RuneError { 53 return false 54 } 55 s = s[size:] 56 if !equalFoldRune(sr, pr) { 57 return false 58 } 59 } 60 return true 61 } 62 63 // HasSuffixFold is like strings.HasSuffix but uses Unicode case-folding, 64 // matching case insensitively. 65 func HasSuffixFold(s, suffix string) bool { 66 if suffix == "" { 67 return true 68 } 69 // count the runes and bytes in s, but only till rune count of suffix 70 bo, so := len(s), len(suffix) 71 for bo > 0 && so > 0 { 72 r, size := utf8.DecodeLastRuneInString(s[:bo]) 73 if r == utf8.RuneError { 74 return false 75 } 76 bo -= size 77 78 sr, size := utf8.DecodeLastRuneInString(suffix[:so]) 79 if sr == utf8.RuneError { 80 return false 81 } 82 so -= size 83 84 if !equalFoldRune(r, sr) { 85 return false 86 } 87 } 88 return so == 0 89 } 90 91 // ContainsFold is like strings.Contains but uses Unicode case-folding. 92 func ContainsFold(s, substr string) bool { 93 if substr == "" { 94 return true 95 } 96 if s == "" { 97 return false 98 } 99 firstRune := rune(substr[0]) 100 if firstRune >= utf8.RuneSelf { 101 firstRune, _ = utf8.DecodeRuneInString(substr) 102 } 103 for i, rune := range s { 104 if equalFoldRune(rune, firstRune) && HasPrefixFold(s[i:], substr) { 105 return true 106 } 107 } 108 return false 109 } 110 111 // Reverse returns a new string of the rune characters from the given string 112 // in reverse order. 113 func Reverse(s string) string { 114 runes := []rune(s) 115 length := len(runes) 116 i, j := 0, length-1 117 for i < j { 118 runes[i], runes[j] = runes[j], runes[i] 119 i++ 120 j-- 121 } 122 return string(runes) 123 }