github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/tolower_go112.go (about)

     1  // Copyright 2009 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 "third_party_licenses/Go (golang)" file.
     4  
     5  //+build go1.12
     6  
     7  package gramework
     8  
     9  import (
    10  	"strings"
    11  	"unicode"
    12  	"unicode/utf8"
    13  )
    14  
    15  // toLower returns a copy of the string s with all Unicode letters mapped to their lower case.
    16  func toLower(s string) string {
    17  	isASCII, hasUpper := true, false
    18  	for i := 0; i < len(s); i++ {
    19  		c := s[i]
    20  		if c >= utf8.RuneSelf {
    21  			isASCII = false
    22  			break
    23  		}
    24  		hasUpper = hasUpper || (c >= 'A' && c <= 'Z')
    25  	}
    26  
    27  	if isASCII { // optimize for ASCII-only strings.
    28  		if !hasUpper {
    29  			return s
    30  		}
    31  		var b strings.Builder
    32  		b.Grow(len(s))
    33  		for i := 0; i < len(s); i++ {
    34  			c := s[i]
    35  			if c >= 'A' && c <= 'Z' {
    36  				c += 'a' - 'A'
    37  			}
    38  			b.WriteByte(c)
    39  		}
    40  		return b.String()
    41  	}
    42  	return stringsMap(unicode.ToLower, s)
    43  }
    44  
    45  // Map returns a copy of the string s with all its characters modified
    46  // according to the mapping function. If mapping returns a negative value, the character is
    47  // dropped from the string with no replacement.
    48  func stringsMap(mapping func(rune) rune, s string) string {
    49  	// In the worst case, the string can grow when mapped, making
    50  	// things unpleasant. But it's so rare we barge in assuming it's
    51  	// fine. It could also shrink but that falls out naturally.
    52  
    53  	// The output buffer b is initialized on demand, the first
    54  	// time a character differs.
    55  	var b strings.Builder
    56  
    57  	for i, c := range s {
    58  		r := mapping(c)
    59  		if r == c {
    60  			continue
    61  		}
    62  
    63  		b.Grow(len(s) + utf8.UTFMax)
    64  		b.WriteString(s[:i])
    65  		if r >= 0 {
    66  			b.WriteRune(r)
    67  		}
    68  
    69  		if c == utf8.RuneError {
    70  			// RuneError is the result of either decoding
    71  			// an invalid sequence or '\uFFFD'. Determine
    72  			// the correct number of bytes we need to advance.
    73  			_, w := utf8.DecodeRuneInString(s[i:])
    74  			i += w
    75  		} else {
    76  			i += utf8.RuneLen(c)
    77  		}
    78  
    79  		s = s[i:]
    80  		break
    81  	}
    82  
    83  	// Fast path for unchanged input
    84  	if b.Cap() == 0 { // didn't call b.Grow above
    85  		return s
    86  	}
    87  
    88  	for _, c := range s {
    89  		r := mapping(c)
    90  
    91  		if r >= 0 {
    92  			// common case
    93  			// Due to inlining, it is more performant to determine if WriteByte should be
    94  			// invoked rather than always call WriteRune
    95  			if r < utf8.RuneSelf {
    96  				b.WriteByte(byte(r))
    97  			} else {
    98  				// r is not a ASCII rune.
    99  				b.WriteRune(r)
   100  			}
   101  		}
   102  	}
   103  
   104  	return b.String()
   105  }