mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-10 10:03:43 +00:00
9e2819b5d4
Noted as useful during review of #14448. Updates #14457 Change-Id: I0f16f08d5b05a8e9044b19ef6c02d3dab497f131 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
53 lines
1.3 KiB
Go
53 lines
1.3 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package stringsx provides additional string manipulation functions
|
|
// that aren't in the standard library's strings package or go4.org/mem.
|
|
package stringsx
|
|
|
|
import (
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// CompareFold returns -1, 0, or 1 depending on whether a < b, a == b, or a > b,
|
|
// like cmp.Compare, but case insensitively.
|
|
func CompareFold(a, b string) int {
|
|
// Track our position in both strings
|
|
ia, ib := 0, 0
|
|
for ia < len(a) && ib < len(b) {
|
|
ra, wa := nextRuneLower(a[ia:])
|
|
rb, wb := nextRuneLower(b[ib:])
|
|
if ra < rb {
|
|
return -1
|
|
}
|
|
if ra > rb {
|
|
return 1
|
|
}
|
|
ia += wa
|
|
ib += wb
|
|
if wa == 0 || wb == 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
// If we've reached here, one or both strings are exhausted
|
|
// The shorter string is "less than" if they match up to this point
|
|
switch {
|
|
case ia == len(a) && ib == len(b):
|
|
return 0
|
|
case ia == len(a):
|
|
return -1
|
|
default:
|
|
return 1
|
|
}
|
|
}
|
|
|
|
// nextRuneLower returns the next rune in the string, lowercased, along with its
|
|
// original (consumed) width in bytes. If the string is empty, it returns
|
|
// (utf8.RuneError, 0)
|
|
func nextRuneLower(s string) (r rune, width int) {
|
|
r, width = utf8.DecodeRuneInString(s)
|
|
return unicode.ToLower(r), width
|
|
}
|