From 6fcf9cd2390965211e617d5a3f11eb88209f38bf Mon Sep 17 00:00:00 2001
From: Jason McNeil <sixcolors@mac.com>
Date: Mon, 25 Mar 2024 12:37:01 -0300
Subject: [PATCH 1/3] feat: Add IfToLower and IfToUpper functions for
 converting ASCII strings to lowercase and uppercase respectively

---
 strings.go      | 58 +++++++++++++++++++++++++++++++++++++++++++++
 strings_test.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+)

diff --git a/strings.go b/strings.go
index 0436f03..2165898 100644
--- a/strings.go
+++ b/strings.go
@@ -68,3 +68,61 @@ func DefaultString(value string, defaultValue string) string {
 	}
 	return value
 }
+
+// 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)
+}
diff --git a/strings_test.go b/strings_test.go
index c65142b..a30bbff 100644
--- a/strings_test.go
+++ b/strings_test.go
@@ -25,6 +25,18 @@ func Benchmark_ToUpper(b *testing.B) {
 		}
 		AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", 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(path)
@@ -56,6 +68,18 @@ func Benchmark_ToLower(b *testing.B) {
 		}
 		AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", 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(path)
@@ -147,3 +171,42 @@ func Benchmark_Trim(b *testing.B) {
 		AssertEqual(b, "foobar", 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)
+	})
+}

From 5a92a79d04aa7d8e1d4bbd3f3820cdbcb34cf19f Mon Sep 17 00:00:00 2001
From: Jason McNeil <sixcolors@mac.com>
Date: Mon, 25 Mar 2024 13:06:15 -0300
Subject: [PATCH 2/3] refactor: DRY IfToLower and IfToUpper

---
 strings.go | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/strings.go b/strings.go
index 2165898..bf59f9a 100644
--- a/strings.go
+++ b/strings.go
@@ -89,13 +89,7 @@ func IfToLower(s string) string {
 	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)
+	return ToLower(s)
 }
 
 // IfToUpper returns an uppercase version of the input ASCII string.
@@ -118,11 +112,5 @@ func IfToUpper(s string) string {
 	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)
+	return ToUpper(s)
 }

From f0cce84d688a8f7a0dcc4c0df789f9a94e1bc258 Mon Sep 17 00:00:00 2001
From: Jason McNeil <sixcolors@mac.com>
Date: Mon, 25 Mar 2024 13:18:04 -0300
Subject: [PATCH 3/3] feat: Backport IfToLower and IfToUpper functions from v2

---
 strings_test.go | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/strings_test.go b/strings_test.go
index a30bbff..5b0fece 100644
--- a/strings_test.go
+++ b/strings_test.go
@@ -27,15 +27,15 @@ func Benchmark_ToUpper(b *testing.B) {
 	})
 	b.Run("IfToUpper-Upper", func(b *testing.B) {
 		for n := 0; n < b.N; n++ {
-			res = IfToUpper(upperStr)
+			res = IfToUpper("/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS")
 		}
-		require.Equal(b, upperStr, res)
+		AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res)
 	})
 	b.Run("IfToUpper-Mixed", func(b *testing.B) {
 		for n := 0; n < b.N; n++ {
-			res = IfToUpper(largeStr)
+			res = IfToUpper(path)
 		}
-		require.Equal(b, upperStr, res)
+		AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res)
 	})
 	b.Run("default", func(b *testing.B) {
 		for n := 0; n < b.N; n++ {
@@ -70,15 +70,15 @@ func Benchmark_ToLower(b *testing.B) {
 	})
 	b.Run("IfToLower-Lower", func(b *testing.B) {
 		for n := 0; n < b.N; n++ {
-			res = IfToLower(lowerStr)
+			res = IfToLower("/repos/gofiber/fiber/issues/187643/comments")
 		}
-		require.Equal(b, lowerStr, res)
+		AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res)
 	})
 	b.Run("IfToLower-Mixed", func(b *testing.B) {
 		for n := 0; n < b.N; n++ {
-			res = IfToLower(largeStr)
+			res = IfToLower(path)
 		}
-		require.Equal(b, lowerStr, res)
+		AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res)
 	})
 	b.Run("default", func(b *testing.B) {
 		for n := 0; n < b.N; n++ {
@@ -174,17 +174,17 @@ func Benchmark_Trim(b *testing.B) {
 
 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
+	AssertEqual(t, "MYNAMEISPARAM", IfToUpper("MYNAMEISPARAM")) // already uppercase
+	AssertEqual(t, "MYNAMEISPARAM", IfToUpper("mynameisparam")) // lowercase to uppercase
+	AssertEqual(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
+	AssertEqual(t, "mynameisparam", IfToLower("mynameisparam"))           // already lowercase
+	AssertEqual(t, "mynameisparam", IfToLower("myNameIsParam"))           // mixed case
+	AssertEqual(t, "https://gofiber.io", IfToLower("https://gofiber.io")) // Origin Header Type URL
+	AssertEqual(t, "mynameisparam", IfToLower("MYNAMEISPARAM"))           // uppercase to lowercase
 }
 
 // Benchmark_IfToLower_HeadersOrigin benchmarks the IfToLower function with an origin header type URL.
@@ -195,18 +195,18 @@ func Benchmark_IfToToLower_HeadersOrigin(b *testing.B) {
 		for n := 0; n < b.N; n++ {
 			res = ToLower("https://gofiber.io")
 		}
-		require.Equal(b, "https://gofiber.io", res)
+		AssertEqual(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)
+		AssertEqual(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)
+		AssertEqual(b, "https://gofiber.io", res)
 	})
 }