Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add IfToLower and IfToUpper functions for converting ASCII strings to lowercase and uppercase respectively #76

Merged
merged 2 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,61 @@ 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
}
res := make([]byte, len(s))
copy(res, s)
for i := 0; i < len(res); i++ {
res[i] = toLowerTable[res[i]]
}

return UnsafeString(res)
}

// 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
}
res := make([]byte, len(s))
copy(res, s)
for i := 0; i < len(res); i++ {
res[i] = toUpperTable[res[i]]
}

return UnsafeString(res)
}
63 changes: 63 additions & 0 deletions strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -55,10 +67,61 @@ 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)
}
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)
})
}
Loading