github.com/llir/llvm@v0.3.6/internal/natsort/natsort.go (about)

     1  // Package natsort implements natural sort. In "Natural Sort Order" integers
     2  // embedded in strings are compared by value.
     3  //
     4  // References:
     5  //    https://blog.codinghorror.com/sorting-for-humans-natural-sort-order/
     6  package natsort
     7  
     8  import (
     9  	"sort"
    10  )
    11  
    12  // Strings sorts the given slice of strings in natural order.
    13  func Strings(a []string) {
    14  	sort.Sort(Order(a))
    15  }
    16  
    17  // Order implements sort.Interface to sort strings in natural order. This means
    18  // that e.g. "abc2" < "abc12".
    19  //
    20  // Non-digit sequences and numbers are compared separately. The former are
    21  // compared bytewise, while the latter are compared numerically (except that
    22  // the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
    23  //
    24  // Limitation: only ASCII digits (0-9) are considered.
    25  type Order []string
    26  
    27  func (n Order) Len() int           { return len(n) }
    28  func (n Order) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
    29  func (n Order) Less(i, j int) bool { return Less(n[i], n[j]) }
    30  
    31  // isdigit reports whether the given byte is a decimal digit.
    32  func isdigit(b byte) bool {
    33  	return '0' <= b && b <= '9'
    34  }
    35  
    36  // Less compares two strings using natural ordering. This means that e.g. "abc2"
    37  // < "abc12".
    38  //
    39  // Non-digit sequences and numbers are compared separately. The former are
    40  // compared bytewise, while the latter are compared numerically (except that
    41  // the number of leading zeros is used as a tie-breaker, so e.g. "2" < "02")
    42  //
    43  // Limitation: only ASCII digits (0-9) are considered.
    44  func Less(str1, str2 string) bool {
    45  	idx1, idx2 := 0, 0
    46  	for idx1 < len(str1) && idx2 < len(str2) {
    47  		c1, c2 := str1[idx1], str2[idx2]
    48  		dig1, dig2 := isdigit(c1), isdigit(c2)
    49  		switch {
    50  		case dig1 && dig2: // Digits
    51  			// Eat zeros.
    52  			for ; idx1 < len(str1) && str1[idx1] == '0'; idx1++ {
    53  			}
    54  			for ; idx2 < len(str2) && str2[idx2] == '0'; idx2++ {
    55  			}
    56  			// Eat all digits.
    57  			nonZero1, nonZero2 := idx1, idx2
    58  			for ; idx1 < len(str1) && isdigit(str1[idx1]); idx1++ {
    59  			}
    60  			for ; idx2 < len(str2) && isdigit(str2[idx2]); idx2++ {
    61  			}
    62  			// If lengths of numbers with non-zero prefix differ, the shorter
    63  			// one is less.
    64  			if len1, len2 := idx1-nonZero1, idx2-nonZero2; len1 != len2 {
    65  				return len1 < len2
    66  			}
    67  			// If they're equal, string comparison is correct.
    68  			if nr1, nr2 := str1[nonZero1:idx1], str2[nonZero2:idx2]; nr1 != nr2 {
    69  				return nr1 < nr2
    70  			}
    71  			// Otherwise, the one with less zeros is less.
    72  			// Because everything up to the number is equal, comparing the index
    73  			// after the zeros is sufficient.
    74  			if nonZero1 != nonZero2 {
    75  				return nonZero1 < nonZero2
    76  			}
    77  		default: // non-digit characters
    78  			// UTF-8 compares bytewise-lexicographically, no need to decode
    79  			// codepoints.
    80  			if c1 != c2 {
    81  				return c1 < c2
    82  			}
    83  			idx1++
    84  			idx2++
    85  		}
    86  		// They're identical so far, so continue comparing.
    87  	}
    88  	// So far they are identical. At least one is ended. If the other continues,
    89  	// it sorts last.
    90  	return len(str1) < len(str2)
    91  }