diff --git a/strings.go b/strings.go index 364c25f..47af92d 100644 --- a/strings.go +++ b/strings.go @@ -25,3 +25,49 @@ func ToUpper(b string) string { return UnsafeString(res) } + +// IfToUpper returns an lowercase version of the input ASCII string. +// +// It first checks if the string contains any uppercase characters before converting it. +// +// For strings that are already lowercase,this function will be faster than `ToLower`. +// +// In the case of mixed-case or uppercase strings, this function will be slightly slower than `ToLower`. +func IfToLower(s string) string { + hasUpper := false + for i := 0; i < len(s); i++ { + c := s[i] + if toLowerTable[c] != c { + hasUpper = true + break + } + } + + if !hasUpper { + return s + } + return ToLower(s) +} + +// IfToUpper returns an uppercase version of the input ASCII string. +// +// It first checks if the string contains any lowercase characters before converting it. +// +// For strings that are already uppercase,this function will be faster than `ToUpper`. +// +// In the case of mixed-case or lowercase strings, this function will be slightly slower than `ToUpper`. +func IfToUpper(s string) string { + hasLower := false + for i := 0; i < len(s); i++ { + c := s[i] + if toUpperTable[c] != c { + hasLower = true + break + } + } + + if !hasLower { + return s + } + return ToUpper(s) +} diff --git a/strings_test.go b/strings_test.go index 37abc96..f837b2d 100644 --- a/strings_test.go +++ b/strings_test.go @@ -30,6 +30,18 @@ func Benchmark_ToUpper(b *testing.B) { } require.Equal(b, upperStr, res) }) + b.Run("IfToUpper-Upper", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = IfToUpper(upperStr) + } + require.Equal(b, upperStr, res) + }) + b.Run("IfToUpper-Mixed", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = IfToUpper(largeStr) + } + require.Equal(b, upperStr, res) + }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = strings.ToUpper(largeStr) @@ -55,6 +67,18 @@ func Benchmark_ToLower(b *testing.B) { } require.Equal(b, lowerStr, res) }) + b.Run("IfToLower-Lower", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = IfToLower(lowerStr) + } + require.Equal(b, lowerStr, res) + }) + b.Run("IfToLower-Mixed", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = IfToLower(largeStr) + } + require.Equal(b, lowerStr, res) + }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = strings.ToLower(largeStr) @@ -62,3 +86,42 @@ func Benchmark_ToLower(b *testing.B) { require.Equal(b, lowerStr, res) }) } + +func Test_IfToUpper(t *testing.T) { + t.Parallel() + require.Equal(t, "MYNAMEISPARAM", IfToUpper("MYNAMEISPARAM")) // already uppercase + require.Equal(t, "MYNAMEISPARAM", IfToUpper("mynameisparam")) // lowercase to uppercase + require.Equal(t, "MYNAMEISPARAM", IfToUpper("MyNameIsParam")) // mixed case +} + +func Test_IfToLower(t *testing.T) { + t.Parallel() + require.Equal(t, "mynameisparam", IfToLower("mynameisparam")) // already lowercase + require.Equal(t, "mynameisparam", IfToLower("myNameIsParam")) // mixed case + require.Equal(t, "https://gofiber.io", IfToLower("https://gofiber.io")) // Origin Header Type URL + require.Equal(t, "mynameisparam", IfToLower("MYNAMEISPARAM")) // uppercase to lowercase +} + +// Benchmark_IfToLower_HeadersOrigin benchmarks the IfToLower function with an origin header type URL. +// These headers are typically lowercase, so the function should return the input string without modification. +func Benchmark_IfToToLower_HeadersOrigin(b *testing.B) { + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToLower("https://gofiber.io") + } + require.Equal(b, "https://gofiber.io", res) + }) + b.Run("IfToLower-Lower", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = IfToLower("https://gofiber.io") + } + require.Equal(b, "https://gofiber.io", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.ToLower("https://gofiber.io") + } + require.Equal(b, "https://gofiber.io", res) + }) +}