github.com/linchen2chris/hugo@v0.0.0-20230307053224-cec209389705/compare/compare_strings.go (about)

     1  // Copyright 2019 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package compare
    15  
    16  import (
    17  	"strings"
    18  	"unicode"
    19  	"unicode/utf8"
    20  )
    21  
    22  // Strings returns an integer comparing two strings lexicographically.
    23  func Strings(s, t string) int {
    24  	c := compareFold(s, t)
    25  
    26  	if c == 0 {
    27  		// "B" and "b" would be the same so we need a tiebreaker.
    28  		return strings.Compare(s, t)
    29  	}
    30  
    31  	return c
    32  }
    33  
    34  // This function is derived from strings.EqualFold in Go's stdlib.
    35  // https://github.com/golang/go/blob/ad4a58e31501bce5de2aad90a620eaecdc1eecb8/src/strings/strings.go#L893
    36  func compareFold(s, t string) int {
    37  	for s != "" && t != "" {
    38  		var sr, tr rune
    39  		if s[0] < utf8.RuneSelf {
    40  			sr, s = rune(s[0]), s[1:]
    41  		} else {
    42  			r, size := utf8.DecodeRuneInString(s)
    43  			sr, s = r, s[size:]
    44  		}
    45  		if t[0] < utf8.RuneSelf {
    46  			tr, t = rune(t[0]), t[1:]
    47  		} else {
    48  			r, size := utf8.DecodeRuneInString(t)
    49  			tr, t = r, t[size:]
    50  		}
    51  
    52  		if tr == sr {
    53  			continue
    54  		}
    55  
    56  		c := 1
    57  		if tr < sr {
    58  			tr, sr = sr, tr
    59  			c = -c
    60  		}
    61  
    62  		//  ASCII only.
    63  		if tr < utf8.RuneSelf {
    64  			if sr >= 'A' && sr <= 'Z' {
    65  				if tr <= 'Z' {
    66  					// Same case.
    67  					return -c
    68  				}
    69  
    70  				diff := tr - (sr + 'a' - 'A')
    71  
    72  				if diff == 0 {
    73  					continue
    74  				}
    75  
    76  				if diff < 0 {
    77  					return c
    78  				}
    79  
    80  				if diff > 0 {
    81  					return -c
    82  				}
    83  			}
    84  		}
    85  
    86  		// Unicode.
    87  		r := unicode.SimpleFold(sr)
    88  		for r != sr && r < tr {
    89  			r = unicode.SimpleFold(r)
    90  		}
    91  
    92  		if r == tr {
    93  			continue
    94  		}
    95  
    96  		return -c
    97  	}
    98  
    99  	if s == "" && t == "" {
   100  		return 0
   101  	}
   102  
   103  	if s == "" {
   104  		return -1
   105  	}
   106  
   107  	return 1
   108  }
   109  
   110  // LessStrings returns whether s is less than t lexicographically.
   111  func LessStrings(s, t string) bool {
   112  	return Strings(s, t) < 0
   113  }