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

v3(enhancement): remove utils.Trim* because stdlib has same performance in go1.19 #2030

Merged
merged 17 commits into from
Aug 20, 2022
4 changes: 2 additions & 2 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ func (c *DefaultCtx) WriteString(s string) (int, error) {
// XHR returns a Boolean property, that is true, if the request's X-Requested-With header field is XMLHttpRequest,
// indicating that the request was issued by a client library (such as jQuery).
func (c *DefaultCtx) XHR() bool {
return utils.EqualFoldBytes(utils.UnsafeBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest"))
return utils.EqualFold(c.Get(HeaderXRequestedWith), "xmlhttprequest")
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we use EqualFoldBytes instead of EqualFold in the first place?

}

// configDependentPaths set paths for route recognition and prepared paths for the user,
Expand All @@ -1267,7 +1267,7 @@ func (c *DefaultCtx) configDependentPaths() {
}
// If StrictRouting is disabled, we strip all trailing slashes
if !c.app.config.StrictRouting && len(c.detectionPathBuffer) > 1 && c.detectionPathBuffer[len(c.detectionPathBuffer)-1] == '/' {
c.detectionPathBuffer = utils.TrimRightBytes(c.detectionPathBuffer, '/')
c.detectionPathBuffer = utils.TrimRight(c.detectionPathBuffer, '/')
}
c.detectionPath = c.app.getString(c.detectionPathBuffer)

Expand Down
163 changes: 78 additions & 85 deletions utils/README.md

Large diffs are not rendered by default.

48 changes: 0 additions & 48 deletions utils/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,51 +19,3 @@ func ToUpperBytes(b []byte) []byte {
}
return b
}

// TrimRightBytes is the equivalent of bytes.TrimRight
func TrimRightBytes(b []byte, cutset byte) []byte {
lenStr := len(b)
for lenStr > 0 && b[lenStr-1] == cutset {
lenStr--
}
return b[:lenStr]
}

// TrimLeftBytes is the equivalent of bytes.TrimLeft
func TrimLeftBytes(b []byte, cutset byte) []byte {
lenStr, start := len(b), 0
for start < lenStr && b[start] == cutset {
start++
}
return b[start:]
}

// TrimBytes is the equivalent of bytes.Trim
func TrimBytes(b []byte, cutset byte) []byte {
i, j := 0, len(b)-1
for ; i <= j; i++ {
if b[i] != cutset {
break
}
}
for ; i < j; j-- {
if b[j] != cutset {
break
}
}

return b[i : j+1]
}

// EqualFoldBytes tests ascii slices for equality case-insensitively
func EqualFoldBytes(b, s []byte) bool {
if len(b) != len(s) {
return false
}
for i := len(b) - 1; i >= 0; i-- {
if toUpperTable[b[i]] != toUpperTable[s[i]] {
return false
}
}
return true
}
144 changes: 0 additions & 144 deletions utils/bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,147 +72,3 @@ func Benchmark_ToUpperBytes(b *testing.B) {
AssertEqual(b, bytes.Equal(want, res), true)
})
}

func Test_TrimRightBytes(t *testing.T) {
t.Parallel()
res := TrimRightBytes([]byte("/test//////"), '/')
AssertEqual(t, []byte("/test"), res)

res = TrimRightBytes([]byte("/test"), '/')
AssertEqual(t, []byte("/test"), res)

res = TrimRightBytes([]byte(" "), ' ')
AssertEqual(t, 0, len(res))

res = TrimRightBytes([]byte(" "), ' ')
AssertEqual(t, 0, len(res))

res = TrimRightBytes([]byte(""), ' ')
AssertEqual(t, 0, len(res))
}

func Benchmark_TrimRightBytes(b *testing.B) {
var res []byte

b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = TrimRightBytes([]byte("foobar "), ' ')
}
AssertEqual(b, []byte("foobar"), res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = bytes.TrimRight([]byte("foobar "), " ")
}
AssertEqual(b, []byte("foobar"), res)
})
}

func Test_TrimLeftBytes(t *testing.T) {
t.Parallel()
res := TrimLeftBytes([]byte("////test/"), '/')
AssertEqual(t, []byte("test/"), res)

res = TrimLeftBytes([]byte("test/"), '/')
AssertEqual(t, []byte("test/"), res)

res = TrimLeftBytes([]byte(" "), ' ')
AssertEqual(t, 0, len(res))

res = TrimLeftBytes([]byte(" "), ' ')
AssertEqual(t, 0, len(res))

res = TrimLeftBytes([]byte(""), ' ')
AssertEqual(t, 0, len(res))
}

func Benchmark_TrimLeftBytes(b *testing.B) {
var res []byte

b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = TrimLeftBytes([]byte(" foobar"), ' ')
}
AssertEqual(b, []byte("foobar"), res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = bytes.TrimLeft([]byte(" foobar"), " ")
}
AssertEqual(b, []byte("foobar"), res)
})
}

func Test_TrimBytes(t *testing.T) {
t.Parallel()
res := TrimBytes([]byte(" test "), ' ')
AssertEqual(t, []byte("test"), res)

res = TrimBytes([]byte("test"), ' ')
AssertEqual(t, []byte("test"), res)

res = TrimBytes([]byte(".test"), '.')
AssertEqual(t, []byte("test"), res)

res = TrimBytes([]byte(" "), ' ')
AssertEqual(t, 0, len(res))

res = TrimBytes([]byte(" "), ' ')
AssertEqual(t, 0, len(res))

res = TrimBytes([]byte(""), ' ')
AssertEqual(t, 0, len(res))
}

func Benchmark_TrimBytes(b *testing.B) {
var res []byte

b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = TrimBytes([]byte(" foobar "), ' ')
}
AssertEqual(b, []byte("foobar"), res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = bytes.Trim([]byte(" foobar "), " ")
}
AssertEqual(b, []byte("foobar"), res)
})
}

func Benchmark_EqualFoldBytes(b *testing.B) {
left := []byte(upperStr)
right := []byte(lowerStr)
var res bool
b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = EqualFoldBytes(left, right)
}
AssertEqual(b, true, res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = bytes.EqualFold(left, right)
}
AssertEqual(b, true, res)
})
}

func Test_EqualFoldBytes(t *testing.T) {
t.Parallel()
res := EqualFoldBytes([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*"))
AssertEqual(t, true, res)
res = EqualFoldBytes([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*"))
AssertEqual(t, true, res)
res = EqualFoldBytes([]byte("/my2/name/is/:param/*"), []byte("/my2/name"))
AssertEqual(t, false, res)
res = EqualFoldBytes([]byte("/dddddd"), []byte("eeeeee"))
AssertEqual(t, false, res)
res = EqualFoldBytes([]byte("\na"), []byte("*A"))
AssertEqual(t, false, res)
res = EqualFoldBytes([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*"))
AssertEqual(t, true, res)
res = EqualFoldBytes([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*"))
AssertEqual(t, true, res)
}
53 changes: 53 additions & 0 deletions utils/byteseq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package utils

type byteSeq interface {
~string | ~[]byte
}

// Trim is the equivalent of [strings.Trim] or [bytes.Trim].
func Trim[S byteSeq](s S, cutset byte) S {
i, j := 0, len(s)-1
for ; i <= j; i++ {
if s[i] != cutset {
break
}
}
for ; i < j; j-- {
if s[j] != cutset {
break
}
}

return s[i : j+1]
}

// TrimLeft is the equivalent of [strings.TrimLeft] or [bytes.TrimLeft].
func TrimLeft[S byteSeq](s S, cutset byte) S {
lenStr, start := len(s), 0
for start < lenStr && s[start] == cutset {
start++
}
return s[start:]
}

// TrimRight is the equivalent of [strings.TrimRight] or [bytes.TrimRight].
func TrimRight[S byteSeq](s S, cutset byte) S {
lenStr := len(s)
for lenStr > 0 && s[lenStr-1] == cutset {
lenStr--
}
return s[:lenStr]
}

// EqualFold tests ascii strings or bytes for equality case-insensitively
func EqualFold[S byteSeq](b, s S) bool {
if len(b) != len(s) {
return false
}
for i := len(b) - 1; i >= 0; i-- {
if toUpperTable[b[i]] != toUpperTable[s[i]] {
return false
}
}
return true
}
Loading