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  }