v3(enhancement): remove `utils.Trim*` (#2030)

stdlib functions have same performance in go1.19
pull/2027/head
Trim21 2022-08-20 13:52:09 +08:00 committed by GitHub
parent 73d0b712c8
commit b161f805c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 202 additions and 538 deletions

16
ctx.go
View File

@ -108,9 +108,9 @@ func (c *DefaultCtx) Accepts(offers ...string) string {
for len(header) > 0 && commaPos != -1 {
commaPos = strings.IndexByte(header, ',')
if commaPos != -1 {
spec = utils.Trim(header[:commaPos], ' ')
spec = strings.TrimLeft(header[:commaPos], " ")
} else {
spec = utils.TrimLeft(header, ' ')
spec = strings.TrimLeft(header, " ")
}
if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 {
spec = spec[:factorSign]
@ -508,10 +508,10 @@ func (c *DefaultCtx) IPs() (ips []string) {
for {
commaPos = bytes.IndexByte(header, ',')
if commaPos != -1 {
ips[i] = utils.Trim(c.app.getString(header[:commaPos]), ' ')
ips[i] = strings.Trim(c.app.getString(header[:commaPos]), " ")
header, i = header[commaPos+1:], i+1
} else {
ips[i] = utils.Trim(c.app.getString(header), ' ')
ips[i] = strings.Trim(c.app.getString(header), " ")
return
}
}
@ -526,7 +526,7 @@ func (c *DefaultCtx) Is(extension string) bool {
}
return strings.HasPrefix(
utils.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), ' '),
strings.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), " "),
extensionHeader,
)
}
@ -597,7 +597,7 @@ func (c *DefaultCtx) Links(link ...string) {
_, _ = bb.WriteString(`; rel="` + link[i] + `",`)
}
}
c.setCanonical(HeaderLink, utils.TrimRight(c.app.getString(bb.Bytes()), ','))
c.setCanonical(HeaderLink, strings.TrimRight(c.app.getString(bb.Bytes()), ","))
bytebufferpool.Put(bb)
}
@ -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")
}
// configDependentPaths set paths for route recognition and prepared paths for the user,
@ -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 = bytes.TrimRight(c.detectionPathBuffer, "/")
}
c.detectionPath = c.app.getString(c.detectionPathBuffer)

View File

@ -17,7 +17,6 @@ import (
"time"
"unsafe"
"github.com/gofiber/fiber/v3/utils"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp"
)
@ -223,7 +222,7 @@ func getGroupPath(prefix, path string) string {
path = "/" + path
}
return utils.TrimRight(prefix, '/') + path
return strings.TrimRight(prefix, "/") + path
}
// return valid offer for header negotiation
@ -238,7 +237,7 @@ func getOffer(header string, offers ...string) string {
for len(header) > 0 && commaPos != -1 {
commaPos = strings.IndexByte(header, ',')
if commaPos != -1 {
spec = utils.Trim(header[:commaPos], ' ')
spec = strings.TrimSpace(header[:commaPos])
} else {
spec = header
}

View File

@ -8,7 +8,6 @@ import (
"sync"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/utils"
)
// Config defines the config for middleware.
@ -133,7 +132,7 @@ func New(config ...Config) fiber.Handler {
)
if len(path) > 1 {
path = utils.TrimRight(path, '/')
path = strings.TrimRight(path, "/")
}
file, err = cfg.Root.Open(path)
if err != nil && os.IsNotExist(err) && cfg.NotFoundFile != "" {
@ -153,7 +152,7 @@ func New(config ...Config) fiber.Handler {
// Serve index if path is directory
if stat.IsDir() {
indexPath := utils.TrimRight(path, '/') + cfg.Index
indexPath := strings.TrimRight(path, "/") + cfg.Index
index, err := cfg.Root.Open(indexPath)
if err == nil {
indexStat, err := index.Stat()
@ -226,7 +225,7 @@ func SendFile(c fiber.Ctx, fs http.FileSystem, path string) (err error) {
// Serve index if path is directory
if stat.IsDir() {
indexPath := utils.TrimRight(path, '/') + ConfigDefault.Index
indexPath := strings.TrimRight(path, "/") + ConfigDefault.Index
index, err := fs.Open(indexPath)
if err == nil {
indexStat, err := index.Stat()

View File

@ -10,7 +10,6 @@ import (
"strings"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/utils"
)
func getFileExtension(path string) string {
@ -41,7 +40,7 @@ func dirList(c fiber.Ctx, f http.File) error {
fmt.Fprint(c, "<ul>")
if len(basePathEscaped) > 1 {
parentPathEscaped := html.EscapeString(utils.TrimRight(c.Path(), '/') + "/..")
parentPathEscaped := html.EscapeString(strings.TrimRight(c.Path(), "/") + "/..")
fmt.Fprintf(c, `<li><a href="%s" class="dir">..</a></li>`, parentPathEscaped)
}

View File

@ -13,7 +13,6 @@ import (
"time"
"unicode"
"github.com/gofiber/fiber/v3/utils"
"github.com/google/uuid"
)
@ -51,6 +50,7 @@ const (
optionalParam byte = '?' // concludes a parameter by name and makes it optional
paramStarterChar byte = ':' // start character for a parameter with name
slashDelimiter byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional
slashDelimiterStr = "/" // separator for the route, unlike the other delimiters this character at the end can be optional
escapeChar byte = '\\' // escape character
paramConstraintStart byte = '<' // start of type constraint for a parameter
paramConstraintEnd byte = '>' // end of type constraint for a parameter
@ -159,7 +159,7 @@ func addParameterMetaInfo(segs []*routeSegment) []*routeSegment {
} else {
comparePart = segs[i].Const
if len(comparePart) > 1 {
comparePart = utils.TrimRight(comparePart, slashDelimiter)
comparePart = strings.TrimRight(comparePart, slashDelimiterStr)
}
}
}
@ -354,7 +354,7 @@ func findNextCharsetPositionConstraint(search string, charset []byte) int {
if char == paramConstraintEnd {
constraintEnd = pos
}
//fmt.Println(string(char))
// fmt.Println(string(char))
if pos != -1 && (pos < nextPosition || nextPosition == -1) {
if pos > constraintStart && pos < constraintEnd {
nextPosition = pos

View File

@ -188,7 +188,7 @@ func (app *App) addPrefixToRoute(prefix string, route *Route) *Route {
}
// Strict routing, remove trailing slashes
if !app.config.StrictRouting && len(prettyPath) > 1 {
prettyPath = utils.TrimRight(prettyPath, '/')
prettyPath = strings.TrimRight(prettyPath, "/")
}
route.Path = prefixedPath
@ -246,7 +246,7 @@ func (app *App) register(method, pathRaw string, handlers ...Handler) Router {
}
// Strict routing, remove trailing slashes
if !app.config.StrictRouting && len(pathPretty) > 1 {
pathPretty = utils.TrimRight(pathPretty, '/')
pathPretty = strings.TrimRight(pathPretty, "/")
}
// Is layer a middleware?
isUse := method == methodUse

View File

@ -2,89 +2,60 @@ A collection of common functions but with better performance, less allocations a
```go
// go test -benchmem -run=^$ -bench=Benchmark_ -count=2
Benchmark_ToLowerBytes/fiber-16 42847654 25.7 ns/op 0 B/op 0 allocs/op
Benchmark_ToLowerBytes/fiber-16 46143196 25.7 ns/op 0 B/op 0 allocs/op
Benchmark_ToLowerBytes/default-16 17387322 67.4 ns/op 48 B/op 1 allocs/op
Benchmark_ToLowerBytes/default-16 17906491 67.4 ns/op 48 B/op 1 allocs/op
Benchmark_ToUpperBytes/fiber-16 46143729 25.7 ns/op 0 B/op 0 allocs/op
Benchmark_ToUpperBytes/fiber-16 47989250 25.6 ns/op 0 B/op 0 allocs/op
Benchmark_ToUpperBytes/default-16 15580854 76.7 ns/op 48 B/op 1 allocs/op
Benchmark_ToUpperBytes/default-16 15381202 76.9 ns/op 48 B/op 1 allocs/op
Benchmark_TrimRightBytes/fiber-16 70572459 16.3 ns/op 8 B/op 1 allocs/op
Benchmark_TrimRightBytes/fiber-16 74983597 16.3 ns/op 8 B/op 1 allocs/op
Benchmark_TrimRightBytes/default-16 16212578 74.1 ns/op 40 B/op 2 allocs/op
Benchmark_TrimRightBytes/default-16 16434686 74.1 ns/op 40 B/op 2 allocs/op
Benchmark_TrimLeftBytes/fiber-16 74983128 16.3 ns/op 8 B/op 1 allocs/op
Benchmark_TrimLeftBytes/fiber-16 74985002 16.3 ns/op 8 B/op 1 allocs/op
Benchmark_TrimLeftBytes/default-16 21047868 56.5 ns/op 40 B/op 2 allocs/op
Benchmark_TrimLeftBytes/default-16 21048015 56.5 ns/op 40 B/op 2 allocs/op
Benchmark_TrimBytes/fiber-16 54533307 21.9 ns/op 16 B/op 1 allocs/op
Benchmark_TrimBytes/fiber-16 54532812 21.9 ns/op 16 B/op 1 allocs/op
Benchmark_TrimBytes/default-16 14282517 84.6 ns/op 48 B/op 2 allocs/op
Benchmark_TrimBytes/default-16 14114508 84.7 ns/op 48 B/op 2 allocs/op
Benchmark_EqualFolds/fiber-16 36355153 32.6 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFolds/fiber-16 36355593 32.6 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFolds/default-16 15186220 78.1 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFolds/default-16 15186412 78.3 ns/op 0 B/op 0 allocs/op
Benchmark_UUID/fiber-16 23994625 49.8 ns/op 48 B/op 1 allocs/op
Benchmark_UUID/fiber-16 23994768 50.1 ns/op 48 B/op 1 allocs/op
Benchmark_UUID/default-16 3233772 371 ns/op 208 B/op 6 allocs/op
Benchmark_UUID/default-16 3251295 370 ns/op 208 B/op 6 allocs/op
Benchmark_GetString/unsafe-16 1000000000 0.709 ns/op 0 B/op 0 allocs/op
Benchmark_GetString/unsafe-16 1000000000 0.713 ns/op 0 B/op 0 allocs/op
Benchmark_GetString/default-16 59986202 19.0 ns/op 16 B/op 1 allocs/op
Benchmark_GetString/default-16 63142939 19.0 ns/op 16 B/op 1 allocs/op
Benchmark_GetBytes/unsafe-16 508360195 2.36 ns/op 0 B/op 0 allocs/op
Benchmark_GetBytes/unsafe-16 508359979 2.35 ns/op 0 B/op 0 allocs/op
Benchmark_GetBytes/default-16 46143019 25.7 ns/op 16 B/op 1 allocs/op
Benchmark_GetBytes/default-16 44434734 25.6 ns/op 16 B/op 1 allocs/op
Benchmark_GetMIME/fiber-16 21423750 56.3 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/fiber-16 21423559 55.4 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/default-16 6735282 173 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/default-16 6895002 172 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/fiber-16 1000000000 0.766 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/fiber-16 1000000000 0.767 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/default-16 159538528 7.50 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/default-16 159750830 7.51 ns/op 0 B/op 0 allocs/op
Benchmark_ToUpper/fiber-16 22217408 53.3 ns/op 48 B/op 1 allocs/op
Benchmark_ToUpper/fiber-16 22636554 53.2 ns/op 48 B/op 1 allocs/op
Benchmark_ToUpper/default-16 11108600 108 ns/op 48 B/op 1 allocs/op
Benchmark_ToUpper/default-16 11108580 108 ns/op 48 B/op 1 allocs/op
Benchmark_ToLower/fiber-16 23994720 49.8 ns/op 48 B/op 1 allocs/op
Benchmark_ToLower/fiber-16 23994768 50.1 ns/op 48 B/op 1 allocs/op
Benchmark_ToLower/default-16 10808376 110 ns/op 48 B/op 1 allocs/op
Benchmark_ToLower/default-16 10617034 110 ns/op 48 B/op 1 allocs/op
Benchmark_TrimRight/fiber-16 413699521 2.94 ns/op 0 B/op 0 allocs/op
Benchmark_TrimRight/fiber-16 415131687 2.91 ns/op 0 B/op 0 allocs/op
Benchmark_TrimRight/default-16 23994577 49.1 ns/op 32 B/op 1 allocs/op
Benchmark_TrimRight/default-16 24484249 49.4 ns/op 32 B/op 1 allocs/op
Benchmark_TrimLeft/fiber-16 379661170 3.13 ns/op 0 B/op 0 allocs/op
Benchmark_TrimLeft/fiber-16 382079941 3.16 ns/op 0 B/op 0 allocs/op
Benchmark_TrimLeft/default-16 27900877 41.9 ns/op 32 B/op 1 allocs/op
Benchmark_TrimLeft/default-16 28564898 42.0 ns/op 32 B/op 1 allocs/op
Benchmark_Trim/fiber-16 236632856 4.96 ns/op 0 B/op 0 allocs/op
Benchmark_Trim/fiber-16 237570085 4.93 ns/op 0 B/op 0 allocs/op
Benchmark_Trim/default-16 18457221 66.0 ns/op 32 B/op 1 allocs/op
Benchmark_Trim/default-16 18177328 65.9 ns/op 32 B/op 1 allocs/op
Benchmark_Trim/default.trimspace-16 188933770 6.33 ns/op 0 B/op 0 allocs/op
Benchmark_Trim/default.trimspace-16 184007649 6.42 ns/op 0 B/op 0 allocs/op
Benchmark_ConvertToBytes/fiber-8 43773547 24.43 ns/op 0 B/op 0 allocs/op
Benchmark_ConvertToBytes/fiber-8 45849477 25.33 ns/op 0 B/op 0 allocs/op
goos: windows
goarch: amd64
pkg: github.com/gofiber/fiber/v3/utils
cpu: AMD Ryzen 7 5800X 8-Core Processor
Benchmark_ToLowerBytes/fiber-16 51138252 22.61 ns/op 0 B/op 0 allocs/op
Benchmark_ToLowerBytes/fiber-16 52126545 22.63 ns/op 0 B/op 0 allocs/op
Benchmark_ToLowerBytes/default-16 16114736 72.76 ns/op 80 B/op 1 allocs/op
Benchmark_ToLowerBytes/default-16 16651540 73.85 ns/op 80 B/op 1 allocs/op
Benchmark_ToUpperBytes/fiber-16 52127224 22.62 ns/op 0 B/op 0 allocs/op
Benchmark_ToUpperBytes/fiber-16 54283167 22.86 ns/op 0 B/op 0 allocs/op
Benchmark_ToUpperBytes/default-16 14060098 84.12 ns/op 80 B/op 1 allocs/op
Benchmark_ToUpperBytes/default-16 14183122 84.51 ns/op 80 B/op 1 allocs/op
Benchmark_EqualFoldBytes/fiber-16 29240264 41.22 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFoldBytes/fiber-16 28535826 40.84 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFoldBytes/default-16 7929867 150.2 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFoldBytes/default-16 7935478 149.7 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFold/fiber-16 35442768 34.25 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFold/fiber-16 35946870 34.96 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFold/default-16 8942130 133.5 ns/op 0 B/op 0 allocs/op
Benchmark_EqualFold/default-16 8977231 134.3 ns/op 0 B/op 0 allocs/op
Benchmark_UUID/fiber-16 30726213 40.57 ns/op 48 B/op 1 allocs/op
Benchmark_UUID/fiber-16 26539394 40.25 ns/op 48 B/op 1 allocs/op
Benchmark_UUID/default-16 4737199 247.5 ns/op 168 B/op 6 allocs/op
Benchmark_UUID/default-16 4603738 250.8 ns/op 168 B/op 6 allocs/op
Benchmark_ConvertToBytes/fiber-16 62450884 19.41 ns/op 0 B/op 0 allocs/op
Benchmark_ConvertToBytes/fiber-16 52123602 19.53 ns/op 0 B/op 0 allocs/op
Benchmark_UnsafeString/unsafe-16 1000000000 0.4496 ns/op 0 B/op 0 allocs/op
Benchmark_UnsafeString/unsafe-16 1000000000 0.4488 ns/op 0 B/op 0 allocs/op
Benchmark_UnsafeString/default-16 79925935 13.99 ns/op 16 B/op 1 allocs/op
Benchmark_UnsafeString/default-16 85637211 14.35 ns/op 16 B/op 1 allocs/op
Benchmark_UnsafeBytes/unsafe-16 540970148 2.214 ns/op 0 B/op 0 allocs/op
Benchmark_UnsafeBytes/unsafe-16 543356940 2.212 ns/op 0 B/op 0 allocs/op
Benchmark_UnsafeBytes/default-16 68896224 17.19 ns/op 16 B/op 1 allocs/op
Benchmark_UnsafeBytes/default-16 70560426 17.05 ns/op 16 B/op 1 allocs/op
Benchmark_ToString-16 29504036 39.57 ns/op 40 B/op 2 allocs/op
Benchmark_ToString-16 30738334 38.89 ns/op 40 B/op 2 allocs/op
Benchmark_GetMIME/fiber-16 28207086 41.84 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/fiber-16 28165773 41.83 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/default-16 12583132 94.04 ns/op 0 B/op 0 allocs/op
Benchmark_GetMIME/default-16 12829614 93.50 ns/op 0 B/op 0 allocs/op
Benchmark_ParseVendorSpecificContentType/vendorContentType-16 30267411 38.72 ns/op 16 B/op 1 allocs/op
Benchmark_ParseVendorSpecificContentType/vendorContentType-16 28543563 38.60 ns/op 16 B/op 1 allocs/op
Benchmark_ParseVendorSpecificContentType/defaultContentType-16 249869286 4.830 ns/op 0 B/op 0 allocs/op
Benchmark_ParseVendorSpecificContentType/defaultContentType-16 248999592 4.805 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/fiber-16 1000000000 0.6744 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/fiber-16 1000000000 0.6788 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/default-16 446818872 2.664 ns/op 0 B/op 0 allocs/op
Benchmark_StatusMessage/default-16 447009616 2.661 ns/op 0 B/op 0 allocs/op
Benchmark_ToUpper/fiber-16 20480331 56.50 ns/op 80 B/op 1 allocs/op
Benchmark_ToUpper/fiber-16 21541200 56.65 ns/op 80 B/op 1 allocs/op
Benchmark_ToUpper/default-16 8433409 141.2 ns/op 80 B/op 1 allocs/op
Benchmark_ToUpper/default-16 8473737 141.1 ns/op 80 B/op 1 allocs/op
Benchmark_ToLower/fiber-16 27248326 44.68 ns/op 80 B/op 1 allocs/op
Benchmark_ToLower/fiber-16 26918443 44.70 ns/op 80 B/op 1 allocs/op
Benchmark_ToLower/default-16 8447336 141.9 ns/op 80 B/op 1 allocs/op
Benchmark_ToLower/default-16 8423156 140.6 ns/op 80 B/op 1 allocs/op
```

View File

@ -10,4 +10,5 @@ func Test_AssertEqual(t *testing.T) {
t.Parallel()
AssertEqual(nil, []string{}, []string{})
AssertEqual(t, []string{}, []string{})
AssertEqual(t, []byte("aq"), []byte("aq"))
}

View File

@ -4,7 +4,7 @@
package utils
// ToLowerBytes converts ascii slice to lower-case
// ToLowerBytes converts ascii slice to lower-case in-place.
func ToLowerBytes(b []byte) []byte {
for i := 0; i < len(b); i++ {
b[i] = toLowerTable[b[i]]
@ -12,58 +12,10 @@ func ToLowerBytes(b []byte) []byte {
return b
}
// ToUpperBytes converts ascii slice to upper-case
// ToUpperBytes converts ascii slice to upper-case in-place.
func ToUpperBytes(b []byte) []byte {
for i := 0; i < len(b); i++ {
b[i] = toUpperTable[b[i]]
}
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
}

View File

@ -11,16 +11,11 @@ import (
func Test_ToLowerBytes(t *testing.T) {
t.Parallel()
res := ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*"))
AssertEqual(t, true, bytes.Equal([]byte("/my/name/is/:param/*"), res))
res = ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*"))
AssertEqual(t, true, bytes.Equal([]byte("/my1/name/is/:param/*"), res))
res = ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*"))
AssertEqual(t, true, bytes.Equal([]byte("/my2/name/is/:param/*"), res))
res = ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*"))
AssertEqual(t, true, bytes.Equal([]byte("/my3/name/is/:param/*"), res))
res = ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*"))
AssertEqual(t, true, bytes.Equal([]byte("/my4/name/is/:param/*"), res))
AssertEqual(t, []byte("/my/name/is/:param/*"), ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*")))
AssertEqual(t, []byte("/my1/name/is/:param/*"), ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*")))
AssertEqual(t, []byte("/my2/name/is/:param/*"), ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*")))
AssertEqual(t, []byte("/my3/name/is/:param/*"), ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*")))
AssertEqual(t, []byte("/my4/name/is/:param/*"), ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*")))
}
func Benchmark_ToLowerBytes(b *testing.B) {
@ -31,28 +26,23 @@ func Benchmark_ToLowerBytes(b *testing.B) {
for n := 0; n < b.N; n++ {
res = ToLowerBytes(path)
}
AssertEqual(b, bytes.Equal(want, res), true)
AssertEqual(b, want, res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = bytes.ToLower(path)
}
AssertEqual(b, bytes.Equal(want, res), true)
AssertEqual(b, want, res)
})
}
func Test_ToUpperBytes(t *testing.T) {
t.Parallel()
res := ToUpperBytes([]byte("/my/name/is/:param/*"))
AssertEqual(t, true, bytes.Equal([]byte("/MY/NAME/IS/:PARAM/*"), res))
res = ToUpperBytes([]byte("/my1/name/is/:param/*"))
AssertEqual(t, true, bytes.Equal([]byte("/MY1/NAME/IS/:PARAM/*"), res))
res = ToUpperBytes([]byte("/my2/name/is/:param/*"))
AssertEqual(t, true, bytes.Equal([]byte("/MY2/NAME/IS/:PARAM/*"), res))
res = ToUpperBytes([]byte("/my3/name/is/:param/*"))
AssertEqual(t, true, bytes.Equal([]byte("/MY3/NAME/IS/:PARAM/*"), res))
res = ToUpperBytes([]byte("/my4/name/is/:param/*"))
AssertEqual(t, true, bytes.Equal([]byte("/MY4/NAME/IS/:PARAM/*"), res))
AssertEqual(t, []byte("/MY/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my/name/is/:param/*")))
AssertEqual(t, []byte("/MY1/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my1/name/is/:param/*")))
AssertEqual(t, []byte("/MY2/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my2/name/is/:param/*")))
AssertEqual(t, []byte("/MY3/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my3/name/is/:param/*")))
AssertEqual(t, []byte("/MY4/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my4/name/is/:param/*")))
}
func Benchmark_ToUpperBytes(b *testing.B) {
@ -63,156 +53,12 @@ func Benchmark_ToUpperBytes(b *testing.B) {
for n := 0; n < b.N; n++ {
res = ToUpperBytes(path)
}
AssertEqual(b, bytes.Equal(want, res), true)
AssertEqual(b, want, res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = bytes.ToUpper(path)
}
AssertEqual(b, bytes.Equal(want, res), true)
AssertEqual(b, want, res)
})
}
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)
}

18
utils/byteseq.go Normal file
View File

@ -0,0 +1,18 @@
package utils
type byteSeq interface {
~string | ~[]byte
}
// 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
}

86
utils/byteseq_test.go Normal file
View File

@ -0,0 +1,86 @@
package utils
import (
"bytes"
"strings"
"testing"
)
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 = EqualFold(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 := EqualFold([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*"))
AssertEqual(t, true, res)
res = EqualFold([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*"))
AssertEqual(t, true, res)
res = EqualFold([]byte("/my2/name/is/:param/*"), []byte("/my2/name"))
AssertEqual(t, false, res)
res = EqualFold([]byte("/dddddd"), []byte("eeeeee"))
AssertEqual(t, false, res)
res = EqualFold([]byte("\na"), []byte("*A"))
AssertEqual(t, false, res)
res = EqualFold([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*"))
AssertEqual(t, true, res)
res = EqualFold([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*"))
AssertEqual(t, true, res)
}
// go test -v -run=^$ -bench=Benchmark_EqualFold -benchmem -count=4 ./utils/
func Benchmark_EqualFold(b *testing.B) {
var res bool
b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = EqualFold(upperStr, lowerStr)
}
AssertEqual(b, true, res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = strings.EqualFold(upperStr, lowerStr)
}
AssertEqual(b, true, res)
})
}
func Test_EqualFold(t *testing.T) {
t.Parallel()
testCases := []struct {
Expected bool
S1 string
S2 string
}{
{Expected: true, S1: "/MY/NAME/IS/:PARAM/*", S2: "/my/name/is/:param/*"},
{Expected: true, S1: "/MY/NAME/IS/:PARAM/*", S2: "/my/name/is/:param/*"},
{Expected: true, S1: "/MY1/NAME/IS/:PARAM/*", S2: "/MY1/NAME/IS/:PARAM/*"},
{Expected: false, S1: "/my2/name/is/:param/*", S2: "/my2/name"},
{Expected: false, S1: "/dddddd", S2: "eeeeee"},
{Expected: false, S1: "\na", S2: "*A"},
{Expected: true, S1: "/MY3/NAME/IS/:PARAM/*", S2: "/my3/name/is/:param/*"},
{Expected: true, S1: "/MY4/NAME/IS/:PARAM/*", S2: "/my4/nAME/IS/:param/*"},
}
for _, tc := range testCases {
res := EqualFold[string](tc.S1, tc.S2)
AssertEqual(t, tc.Expected, res, "string")
res = EqualFold[[]byte]([]byte(tc.S1), []byte(tc.S2))
AssertEqual(t, tc.Expected, res, "bytes")
}
}

View File

@ -25,55 +25,3 @@ func ToUpper(b string) string {
return UnsafeString(res)
}
// TrimLeft is the equivalent of strings.TrimLeft
func TrimLeft(s string, cutset byte) string {
lenStr, start := len(s), 0
for start < lenStr && s[start] == cutset {
start++
}
return s[start:]
}
// Trim is the equivalent of strings.Trim
func Trim(s string, cutset byte) string {
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]
}
// TrimRight is the equivalent of strings.TrimRight
func TrimRight(s string, cutset byte) string {
lenStr := len(s)
for lenStr > 0 && s[lenStr-1] == cutset {
lenStr--
}
return s[:lenStr]
}
type byteSeq interface {
~string | ~[]byte
}
// 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
}

View File

@ -11,8 +11,7 @@ import (
func Test_ToUpper(t *testing.T) {
t.Parallel()
res := ToUpper("/my/name/is/:param/*")
AssertEqual(t, "/MY/NAME/IS/:PARAM/*", res)
AssertEqual(t, "/MY/NAME/IS/:PARAM/*", ToUpper("/my/name/is/:param/*"))
}
const (
@ -39,16 +38,11 @@ func Benchmark_ToUpper(b *testing.B) {
func Test_ToLower(t *testing.T) {
t.Parallel()
res := ToLower("/MY/NAME/IS/:PARAM/*")
AssertEqual(t, "/my/name/is/:param/*", res)
res = ToLower("/MY1/NAME/IS/:PARAM/*")
AssertEqual(t, "/my1/name/is/:param/*", res)
res = ToLower("/MY2/NAME/IS/:PARAM/*")
AssertEqual(t, "/my2/name/is/:param/*", res)
res = ToLower("/MY3/NAME/IS/:PARAM/*")
AssertEqual(t, "/my3/name/is/:param/*", res)
res = ToLower("/MY4/NAME/IS/:PARAM/*")
AssertEqual(t, "/my4/name/is/:param/*", res)
AssertEqual(t, "/my/name/is/:param/*", ToLower("/MY/NAME/IS/:PARAM/*"))
AssertEqual(t, "/my1/name/is/:param/*", ToLower("/MY1/NAME/IS/:PARAM/*"))
AssertEqual(t, "/my2/name/is/:param/*", ToLower("/MY2/NAME/IS/:PARAM/*"))
AssertEqual(t, "/my3/name/is/:param/*", ToLower("/MY3/NAME/IS/:PARAM/*"))
AssertEqual(t, "/my4/name/is/:param/*", ToLower("/MY4/NAME/IS/:PARAM/*"))
}
func Benchmark_ToLower(b *testing.B) {
@ -66,152 +60,3 @@ func Benchmark_ToLower(b *testing.B) {
AssertEqual(b, lowerStr, res)
})
}
func Test_TrimRight(t *testing.T) {
t.Parallel()
res := TrimRight("/test//////", '/')
AssertEqual(t, "/test", res)
res = TrimRight("/test", '/')
AssertEqual(t, "/test", res)
res = TrimRight(" ", ' ')
AssertEqual(t, "", res)
res = TrimRight(" ", ' ')
AssertEqual(t, "", res)
res = TrimRight("", ' ')
AssertEqual(t, "", res)
}
func Benchmark_TrimRight(b *testing.B) {
var res string
b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = TrimRight("foobar ", ' ')
}
AssertEqual(b, "foobar", res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = strings.TrimRight("foobar ", " ")
}
AssertEqual(b, "foobar", res)
})
}
func Test_TrimLeft(t *testing.T) {
t.Parallel()
res := TrimLeft("////test/", '/')
AssertEqual(t, "test/", res)
res = TrimLeft("test/", '/')
AssertEqual(t, "test/", res)
res = TrimLeft(" ", ' ')
AssertEqual(t, "", res)
res = TrimLeft(" ", ' ')
AssertEqual(t, "", res)
res = TrimLeft("", ' ')
AssertEqual(t, "", res)
}
func Benchmark_TrimLeft(b *testing.B) {
var res string
b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = TrimLeft(" foobar", ' ')
}
AssertEqual(b, "foobar", res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = strings.TrimLeft(" foobar", " ")
}
AssertEqual(b, "foobar", res)
})
}
func Test_Trim(t *testing.T) {
t.Parallel()
res := Trim(" test ", ' ')
AssertEqual(t, "test", res)
res = Trim("test", ' ')
AssertEqual(t, "test", res)
res = Trim(".test", '.')
AssertEqual(t, "test", res)
res = Trim(" ", ' ')
AssertEqual(t, "", res)
res = Trim(" ", ' ')
AssertEqual(t, "", res)
res = Trim("", ' ')
AssertEqual(t, "", res)
}
func Benchmark_Trim(b *testing.B) {
var res string
b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = Trim(" foobar ", ' ')
}
AssertEqual(b, "foobar", res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = strings.Trim(" foobar ", " ")
}
AssertEqual(b, "foobar", res)
})
b.Run("default.trimspace", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = strings.TrimSpace(" foobar ")
}
AssertEqual(b, "foobar", res)
})
}
// go test -v -run=^$ -bench=Benchmark_EqualFold -benchmem -count=4
func Benchmark_EqualFold(b *testing.B) {
var res bool
b.Run("fiber", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = EqualFold(upperStr, lowerStr)
}
AssertEqual(b, true, res)
})
b.Run("default", func(b *testing.B) {
for n := 0; n < b.N; n++ {
res = strings.EqualFold(upperStr, lowerStr)
}
AssertEqual(b, true, res)
})
}
func Test_EqualFold(t *testing.T) {
t.Parallel()
res := EqualFold("/MY/NAME/IS/:PARAM/*", "/my/name/is/:param/*")
AssertEqual(t, true, res)
res = EqualFold("/MY1/NAME/IS/:PARAM/*", "/MY1/NAME/IS/:PARAM/*")
AssertEqual(t, true, res)
res = EqualFold("/my2/name/is/:param/*", "/my2/name")
AssertEqual(t, false, res)
res = EqualFold("/dddddd", "eeeeee")
AssertEqual(t, false, res)
res = EqualFold("\na", "*A")
AssertEqual(t, false, res)
res = EqualFold("/MY3/NAME/IS/:PARAM/*", "/my3/name/is/:param/*")
AssertEqual(t, true, res)
res = EqualFold("/MY4/NAME/IS/:PARAM/*", "/my4/nAME/IS/:param/*")
AssertEqual(t, true, res)
}