| // Copyright 2026 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package sgutil |
| |
| import ( |
| "strconv" |
| "strings" |
| ) |
| |
| // isDigit returns true if the byte is an ASCII digit. |
| func isDigit(b byte) bool { |
| return b >= '0' && b <= '9' |
| } |
| |
| // CompareNatural performs a "natural sort" comparison of two strings. |
| // It compares non-digit sections lexicographically and digit sections |
| // numerically. In the case of string-unequal "equal" strings like |
| // "a01b" and "a1b", strings.Compare breaks the tie. |
| // |
| // It returns: |
| // |
| // -1 if s1 < s2 |
| // 0 if s1 == s2 |
| // +1 if s1 > s2 |
| func CompareNatural(s1, s2 string) int { |
| i, j := 0, 0 |
| len1, len2 := len(s1), len(s2) |
| |
| for i < len1 && j < len2 { |
| // Find a non-digit segment or a number segment in both strings. |
| if isDigit(s1[i]) && isDigit(s2[j]) { |
| // Number segment comparison. |
| numStart1 := i |
| for i < len1 && isDigit(s1[i]) { |
| i++ |
| } |
| num1, _ := strconv.Atoi(s1[numStart1:i]) |
| |
| numStart2 := j |
| for j < len2 && isDigit(s2[j]) { |
| j++ |
| } |
| num2, _ := strconv.Atoi(s2[numStart2:j]) |
| |
| if num1 < num2 { |
| return -1 |
| } |
| if num1 > num2 { |
| return 1 |
| } |
| // "1" < "01". Don't expect it in simdgen, but just in case. |
| if ln1, ln2 := i-numStart1, j-numStart2; ln1 != ln2 { |
| return ln1 - ln2 |
| } |
| // If numbers are equal, continue to the next segment. |
| } else { |
| // Non-digit comparison. |
| if s1[i] < s2[j] { |
| return -1 |
| } |
| if s1[i] > s2[j] { |
| return 1 |
| } |
| i++ |
| j++ |
| } |
| } |
| |
| // deal with a01b vs a1b; there needs to be an order. |
| return strings.Compare(s1, s2) |
| } |