|
- // Copyright 2017 The Gitea Authors. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
-
- package base
-
- import (
- "math/big"
- "unicode/utf8"
- )
-
- // NaturalSortLess compares two strings so that they could be sorted in natural order
- func NaturalSortLess(s1, s2 string) bool {
- var i1, i2 int
- for {
- rune1, j1, end1 := getNextRune(s1, i1)
- rune2, j2, end2 := getNextRune(s2, i2)
- if end1 || end2 {
- return end1 != end2 && end1
- }
- dec1 := isDecimal(rune1)
- dec2 := isDecimal(rune2)
- var less, equal bool
- if dec1 && dec2 {
- i1, i2, less, equal = compareByNumbers(s1, i1, s2, i2)
- } else if !dec1 && !dec2 {
- equal = rune1 == rune2
- less = rune1 < rune2
- i1 = j1
- i2 = j2
- } else {
- return rune1 < rune2
- }
- if !equal {
- return less
- }
- }
- }
-
- func getNextRune(str string, pos int) (rune, int, bool) {
- if pos < len(str) {
- r, w := utf8.DecodeRuneInString(str[pos:])
- // Fallback to ascii
- if r == utf8.RuneError {
- r = rune(str[pos])
- w = 1
- }
- return r, pos + w, false
- }
- return 0, pos, true
- }
-
- func isDecimal(r rune) bool {
- return '0' <= r && r <= '9'
- }
-
- func compareByNumbers(str1 string, pos1 int, str2 string, pos2 int) (i1, i2 int, less, equal bool) {
- var d1, d2 bool = true, true
- var dec1, dec2 string
- for d1 || d2 {
- if d1 {
- r, j, end := getNextRune(str1, pos1)
- if !end && isDecimal(r) {
- dec1 += string(r)
- pos1 = j
- } else {
- d1 = false
- }
- }
- if d2 {
- r, j, end := getNextRune(str2, pos2)
- if !end && isDecimal(r) {
- dec2 += string(r)
- pos2 = j
- } else {
- d2 = false
- }
- }
- }
- less, equal = compareBigNumbers(dec1, dec2)
- return pos1, pos2, less, equal
- }
-
- func compareBigNumbers(dec1, dec2 string) (less, equal bool) {
- d1, _ := big.NewInt(0).SetString(dec1, 10)
- d2, _ := big.NewInt(0).SetString(dec2, 10)
- cmp := d1.Cmp(d2)
- return cmp < 0, cmp == 0
- }
|