// Package natural provides natural (alphanumeric) string comparison.// When both strings have digit sequences at the same position, they are// compared numerically (so "file2" < "file10"); otherwise bytes are// compared one-by-one. No allocations are made.package naturalimport "strings"// Compare returns a negative value if a < b, zero if a == b,// or a positive value if a > b using natural sort ordering.//// When two numeric segments are numerically equal (e.g. "01" vs "1"),// comparison continues with the remaining suffixes, and the padding// difference is kept as a final tie-breaker that only decides strings// that are otherwise equal (e.g. "a01" < "a1", "a0" < "a00"). Deferring// it that way is what keeps the ordering transitive, which SQLite// requires of a collating function.func Compare(a, b string) int {return compare(a, b, false)}// CompareFold is Compare with ASCII case folding, matching SQLite's NOCASE// collation: only A-Z fold, bytes >= 0x80 are compared as-is.func CompareFold(a, b string) int {return compare(a, b, true)}func compare(a, b string, fold bool) int {ia, ib := 0, 0// Set when two runs are numerically equal but differently padded. Applying it// immediately would break transitivity, so it only decides otherwise-equal strings.padTie := 0for ia < len(a) && ib < len(b) {ca, cb := a[ia], b[ib]da, db := isDigit(ca), isDigit(cb)if fold {ca, cb = lower(ca), lower(cb)}switch {case da && db:// Both are in digit sequences — compare numerically.endA := iafor endA < len(a) && isDigit(a[endA]) {endA++}endB := ibfor endB < len(b) && isDigit(b[endB]) {endB++}if c := compareNumbers(a[ia:endA], b[ib:endB]); c != 0 {return c}if t := strings.Compare(a[ia:endA], b[ib:endB]); t != 0 {padTie = t}ia = endAib = endBcase da != db:return int(ca) - int(cb)default:if ca != cb {return int(ca) - int(cb)}ia++ib++}}if c := (len(a) - ia) - (len(b) - ib); c != 0 {return c}return padTie}// compareNumbers compares two digit strings numerically.// Leading zeros are stripped before comparison.func compareNumbers(a, b string) int {// Strip leading zeros.sa := stripZeros(a)sb := stripZeros(b)// Different lengths after stripping means different magnitude.if len(sa) != len(sb) {return len(sa) - len(sb)}// Same length — compare digit by digit.for i := range len(sa) {if sa[i] != sb[i] {return int(sa[i]) - int(sb[i])}}return 0}// stripZeros returns s with leading '0' bytes removed.// If s is all zeros, returns the last byte (a single "0").func stripZeros(s string) string {i := 0for i < len(s) && s[i] == '0' {i++}if i == len(s) && len(s) > 0 {return s[len(s)-1:]}return s[i:]}func isDigit(c byte) bool {return c >= '0' && c <= '9'}func lower(c byte) byte {if c >= 'A' && c <= 'Z' {return c + 'a' - 'A'}return c}