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 }