}
// to maps the rune using the specified case mapping.
-func to(_case int, r rune, caseRange []CaseRange) rune {
+// It additionally reports whether caseRange contained a mapping for r.
+func to(_case int, r rune, caseRange []CaseRange) (mappedRune rune, foundMapping bool) {
if _case < 0 || MaxCase <= _case {
- return ReplacementChar // as reasonable an error as any
+ return ReplacementChar, false // as reasonable an error as any
}
// binary search over ranges
lo := 0
// bit in the sequence offset.
// The constants UpperCase and TitleCase are even while LowerCase
// is odd so we take the low bit from _case.
- return rune(cr.Lo) + ((r-rune(cr.Lo))&^1 | rune(_case&1))
+ return rune(cr.Lo) + ((r-rune(cr.Lo))&^1 | rune(_case&1)), true
}
- return r + delta
+ return r + delta, true
}
if r < rune(cr.Lo) {
hi = m
lo = m + 1
}
}
- return r
+ return r, false
}
// To maps the rune to the specified case: UpperCase, LowerCase, or TitleCase.
func To(_case int, r rune) rune {
- return to(_case, r, CaseRanges)
+ r, _ = to(_case, r, CaseRanges)
+ return r
}
// ToUpper maps the rune to upper case.
// ToUpper maps the rune to upper case giving priority to the special mapping.
func (special SpecialCase) ToUpper(r rune) rune {
- r1 := to(UpperCase, r, []CaseRange(special))
- if r1 == r {
+ r1, hadMapping := to(UpperCase, r, []CaseRange(special))
+ if r1 == r && !hadMapping {
r1 = ToUpper(r)
}
return r1
// ToTitle maps the rune to title case giving priority to the special mapping.
func (special SpecialCase) ToTitle(r rune) rune {
- r1 := to(TitleCase, r, []CaseRange(special))
- if r1 == r {
+ r1, hadMapping := to(TitleCase, r, []CaseRange(special))
+ if r1 == r && !hadMapping {
r1 = ToTitle(r)
}
return r1
// ToLower maps the rune to lower case giving priority to the special mapping.
func (special SpecialCase) ToLower(r rune) rune {
- r1 := to(LowerCase, r, []CaseRange(special))
- if r1 == r {
+ r1, hadMapping := to(LowerCase, r, []CaseRange(special))
+ if r1 == r && !hadMapping {
r1 = ToLower(r)
}
return r1
"fmt"
"runtime"
"sort"
+ "strings"
"testing"
. "unicode"
)
}
}
}
+
+func TestSpecialCaseNoMapping(t *testing.T) {
+ // Issue 25636
+ // no change for rune 'A', zero delta, under upper/lower/title case change.
+ var noChangeForCapitalA = CaseRange{'A', 'A', [MaxCase]rune{0, 0, 0}}
+ got := strings.ToLowerSpecial(SpecialCase([]CaseRange{noChangeForCapitalA}), "ABC")
+ want := "Abc"
+ if got != want {
+ t.Errorf("got %q; want %q", got, want)
+ }
+}