code.gitea.io/gitea@v1.19.3/modules/base/natural_sort.go (about) 1 // Copyright 2017 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package base 5 6 import ( 7 "math/big" 8 "unicode/utf8" 9 ) 10 11 // NaturalSortLess compares two strings so that they could be sorted in natural order 12 func NaturalSortLess(s1, s2 string) bool { 13 var i1, i2 int 14 for { 15 rune1, j1, end1 := getNextRune(s1, i1) 16 rune2, j2, end2 := getNextRune(s2, i2) 17 if end1 || end2 { 18 return end1 != end2 && end1 19 } 20 dec1 := isDecimal(rune1) 21 dec2 := isDecimal(rune2) 22 var less, equal bool 23 if dec1 && dec2 { 24 i1, i2, less, equal = compareByNumbers(s1, i1, s2, i2) 25 } else if !dec1 && !dec2 { 26 equal = rune1 == rune2 27 less = rune1 < rune2 28 i1 = j1 29 i2 = j2 30 } else { 31 return rune1 < rune2 32 } 33 if !equal { 34 return less 35 } 36 } 37 } 38 39 func getNextRune(str string, pos int) (rune, int, bool) { 40 if pos < len(str) { 41 r, w := utf8.DecodeRuneInString(str[pos:]) 42 // Fallback to ascii 43 if r == utf8.RuneError { 44 r = rune(str[pos]) 45 w = 1 46 } 47 return r, pos + w, false 48 } 49 return 0, pos, true 50 } 51 52 func isDecimal(r rune) bool { 53 return '0' <= r && r <= '9' 54 } 55 56 func compareByNumbers(str1 string, pos1 int, str2 string, pos2 int) (i1, i2 int, less, equal bool) { 57 d1, d2 := true, true 58 var dec1, dec2 string 59 for d1 || d2 { 60 if d1 { 61 r, j, end := getNextRune(str1, pos1) 62 if !end && isDecimal(r) { 63 dec1 += string(r) 64 pos1 = j 65 } else { 66 d1 = false 67 } 68 } 69 if d2 { 70 r, j, end := getNextRune(str2, pos2) 71 if !end && isDecimal(r) { 72 dec2 += string(r) 73 pos2 = j 74 } else { 75 d2 = false 76 } 77 } 78 } 79 less, equal = compareBigNumbers(dec1, dec2) 80 return pos1, pos2, less, equal 81 } 82 83 func compareBigNumbers(dec1, dec2 string) (less, equal bool) { 84 d1, _ := big.NewInt(0).SetString(dec1, 10) 85 d2, _ := big.NewInt(0).SetString(dec2, 10) 86 cmp := d1.Cmp(d2) 87 return cmp < 0, cmp == 0 88 }