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