From a08da07bc473fecaf93f427a1719c4a6867bb2ad Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Sat, 31 Oct 2020 10:35:02 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=93=A6=20v0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 141 ++++++----- assertions.go | 62 +++++ assertions_test.go | 13 + bytes.go | 70 ++++++ bytes_test.go | 187 +++++++++++++++ common.go | 83 +++++++ common_test.go | 66 ++++++ convert.go | 104 ++++++++ convert_test.go | 63 +++++ go.mod | 3 - go.sum | 0 http.go | 212 +++++++++++++++++ http_test.go | 98 ++++++++ strings.go | 62 +++++ strings_test.go | 149 ++++++++++++ utils.go | 562 -------------------------------------------- utils_bench_test.go | 304 ------------------------ utils_test.go | 237 ------------------- 18 files changed, 1251 insertions(+), 1165 deletions(-) create mode 100644 assertions.go create mode 100644 assertions_test.go create mode 100644 bytes.go create mode 100644 bytes_test.go create mode 100644 common.go create mode 100644 common_test.go create mode 100644 convert.go create mode 100644 convert_test.go delete mode 100644 go.mod delete mode 100644 go.sum create mode 100644 http.go create mode 100644 http_test.go create mode 100644 strings.go create mode 100644 strings_test.go delete mode 100644 utils.go delete mode 100644 utils_bench_test.go delete mode 100644 utils_test.go diff --git a/README.md b/README.md index 1561e97..c4d222b 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,87 @@ -# Utils +A collection of common functions but with better performance, less allocations and no dependencies created for [Fiber](https://github.com/gofiber/fiber). -[![Release](https://img.shields.io/github/release/gofiber/utils.svg)](https://github.com/gofiber/utils/releases) -[![Discord](https://img.shields.io/discord/704680098577514527?label=Discord&logo=discord&logoColor=white&color=7289DA)](https://gofiber.io/discord) -[![Test](https://github.com/gofiber/utils/workflows/Test/badge.svg)](https://github.com/gofiber/utils/actions?query=workflow%3ATest) -[![Security](https://github.com/gofiber/utils/workflows/Security/badge.svg)](https://github.com/gofiber/utils/actions?query=workflow%3ASecurity) -[![Linter](https://github.com/gofiber/utils/workflows/Linter/badge.svg)](https://github.com/gofiber/utils/actions?query=workflow%3ALinter) +```go +// go test -benchmem -run=^$ -bench=Benchmark_ -count=2 -A collection of common functions but with better performance, less allocations and no dependencies created for [Fiber](https://github.com/gofiber/fiber). +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 -```go -// go test -v -benchmem -run=^$ -bench=Benchmark_ -count=2 - -Benchmark_GetMIME/fiber 14287550 84.2 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/fiber 14819698 78.3 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/default 6459128 184 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/default 6385042 184 ns/op 0 B/op 0 allocs/op - -Benchmark_UUID/fiber 17652744 59.1 ns/op 48 B/op 1 allocs/op -Benchmark_UUID/fiber 19361145 58.5 ns/op 48 B/op 1 allocs/op -Benchmark_UUID/default 4271024 281 ns/op 64 B/op 2 allocs/op -Benchmark_UUID/default 4435306 278 ns/op 64 B/op 2 allocs/op - -Benchmark_ToLower/fiber 22987184 48.2 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/fiber 24491794 49.6 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/default 9232608 123 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/default 9454870 123 ns/op 48 B/op 1 allocs/op - -Benchmark_ToLowerBytes/fiber 44463876 26.1 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/fiber 39997200 26.1 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/default 14879088 77.6 ns/op 48 B/op 1 allocs/op -Benchmark_ToLowerBytes/default 14631433 79.2 ns/op 48 B/op 1 allocs/op - -Benchmark_ToUpper/fiber 22648730 49.4 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/fiber 23084425 48.6 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/default 9520122 124 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/default 9375014 133 ns/op 48 B/op 1 allocs/op - -Benchmark_ToUpperBytes/fiber 44439176 25.6 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/fiber 44458934 25.5 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/default 15347073 74.1 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpperBytes/default 15511370 74.2 ns/op 48 B/op 1 allocs/op - -Benchmark_EqualFolds/fiber 34297864 33.8 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFolds/fiber 34285322 34.0 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFolds/default 12756945 91.8 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFolds/default 13015282 91.1 ns/op 0 B/op 0 allocs/op - -Benchmark_Trim/fiber 207314002 5.85 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/fiber 207386125 5.78 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/default 16506302 68.5 ns/op 32 B/op 1 allocs/op -Benchmark_Trim/default 16669119 68.9 ns/op 32 B/op 1 allocs/op - -Benchmark_TrimLeft/fiber 343254828 3.47 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeft/fiber 344407171 3.45 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeft/default 24999790 46.4 ns/op 32 B/op 1 allocs/op -Benchmark_TrimLeft/default 25001926 45.3 ns/op 32 B/op 1 allocs/op - -Benchmark_TrimRight/fiber 374543056 3.15 ns/op 0 B/op 0 allocs/op -Benchmark_TrimRight/fiber 336067616 3.15 ns/op 0 B/op 0 allocs/op -Benchmark_TrimRight/default 20868186 52.8 ns/op 32 B/op 1 allocs/op -Benchmark_TrimRight/default 21434695 55.1 ns/op 32 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 ``` diff --git a/assertions.go b/assertions.go new file mode 100644 index 0000000..a107a46 --- /dev/null +++ b/assertions.go @@ -0,0 +1,62 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "bytes" + "fmt" + "log" + "path/filepath" + "reflect" + "runtime" + "testing" + "text/tabwriter" +) + +// AssertEqual checks if values are equal +func AssertEqual(t testing.TB, expected interface{}, actual interface{}, description ...string) { + if reflect.DeepEqual(expected, actual) { + return + } + var aType = "" + var bType = "" + if reflect.ValueOf(expected).IsValid() { + aType = reflect.TypeOf(expected).Name() + } + if reflect.ValueOf(actual).IsValid() { + bType = reflect.TypeOf(actual).Name() + } + + testName := "AssertEqual" + if t != nil { + testName = t.Name() + } + + _, file, line, _ := runtime.Caller(1) + + var buf bytes.Buffer + w := tabwriter.NewWriter(&buf, 0, 0, 5, ' ', 0) + fmt.Fprintf(w, "\nTest:\t%s", testName) + fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) + fmt.Fprintf(w, "\nError:\tNot equal") + fmt.Fprintf(w, "\nExpect:\t%v\t[%s]", expected, aType) + fmt.Fprintf(w, "\nResult:\t%v\t[%s]", actual, bType) + + if len(description) > 0 { + fmt.Fprintf(w, "\nDescription:\t%s", description[0]) + } + + result := "" + if err := w.Flush(); err != nil { + result = err.Error() + } else { + result = buf.String() + } + if t != nil { + t.Fatal(result) + } else { + log.Fatal(result) + } +} diff --git a/assertions_test.go b/assertions_test.go new file mode 100644 index 0000000..99585b5 --- /dev/null +++ b/assertions_test.go @@ -0,0 +1,13 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import "testing" + +func Test_Utils_AssertEqual(t *testing.T) { + t.Parallel() + AssertEqual(nil, []string{}, []string{}) + AssertEqual(t, []string{}, []string{}) +} diff --git a/bytes.go b/bytes.go new file mode 100644 index 0000000..d331ac6 --- /dev/null +++ b/bytes.go @@ -0,0 +1,70 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +// ToLowerBytes is the equivalent of bytes.ToLower +func ToLowerBytes(b []byte) []byte { + for i := 0; i < len(b); i++ { + b[i] = toLowerTable[b[i]] + } + return b +} + +// ToUpperBytes is the equivalent of bytes.ToUpper +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] +} + +// EqualFold the equivalent of bytes.EqualFold +func EqualsFold(b, s []byte) (equals bool) { + n := len(b) + equals = n == len(s) + if equals { + for i := 0; i < n; i++ { + if equals = b[i]|0x20 == s[i]|0x20; !equals { + break + } + } + } + return +} diff --git a/bytes_test.go b/bytes_test.go new file mode 100644 index 0000000..4617289 --- /dev/null +++ b/bytes_test.go @@ -0,0 +1,187 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "bytes" + "testing" +) + +func Test_Utils_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)) +} + +func Benchmark_ToLowerBytes(b *testing.B) { + var path = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") + var res []byte + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToLowerBytes(path) + } + AssertEqual(b, bytes.Equal(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.ToLower(path) + } + AssertEqual(b, bytes.Equal(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) + }) +} + +func Test_Utils_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)) +} + +func Benchmark_ToUpperBytes(b *testing.B) { + var path = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") + var res []byte + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToUpperBytes(path) + } + AssertEqual(b, bytes.Equal(GetBytes("/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS"), res), true) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.ToUpper(path) + } + AssertEqual(b, bytes.Equal(GetBytes("/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS"), res), true) + }) +} + +func Test_Utils_TrimRightBytes(t *testing.T) { + t.Parallel() + res := TrimRightBytes([]byte("/test//////"), '/') + AssertEqual(t, []byte("/test"), res) + + res = TrimRightBytes([]byte("/test"), '/') + AssertEqual(t, []byte("/test"), 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_Utils_TrimLeftBytes(t *testing.T) { + t.Parallel() + res := TrimLeftBytes([]byte("////test/"), '/') + AssertEqual(t, []byte("test/"), res) + + res = TrimLeftBytes([]byte("test/"), '/') + AssertEqual(t, []byte("test/"), 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_Utils_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) +} +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_EqualFolds(b *testing.B) { + var left = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") + var right = []byte("/RePos/goFiber/Fiber/issues/187643/COMMENTS") + var res bool + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = EqualsFold(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_Utils_EqualsFold(t *testing.T) { + t.Parallel() + res := EqualsFold([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*")) + AssertEqual(t, true, res) + res = EqualsFold([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*")) + AssertEqual(t, true, res) + res = EqualsFold([]byte("/my2/name/is/:param/*"), []byte("/my2/name")) + AssertEqual(t, false, res) + res = EqualsFold([]byte("/dddddd"), []byte("eeeeee")) + AssertEqual(t, false, res) + res = EqualsFold([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*")) + AssertEqual(t, true, res) + res = EqualsFold([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*")) + AssertEqual(t, true, res) +} diff --git a/common.go b/common.go new file mode 100644 index 0000000..323cc2b --- /dev/null +++ b/common.go @@ -0,0 +1,83 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "crypto/rand" + "encoding/binary" + "encoding/hex" + "os" + "reflect" + "runtime" + "sync" + "sync/atomic" +) + +const toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" +const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + +// Copyright © 2014, Roger Peppe +// github.com/rogpeppe/fastuuid +// All rights reserved. + +var uuidSeed [24]byte +var uuidCounter uint64 +var uuidSetup sync.Once + +// UUID generates an universally unique identifier (UUID) +func UUID() string { + // Setup seed & counter once + uuidSetup.Do(func() { + if _, err := rand.Read(uuidSeed[:]); err != nil { + return + } + uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) + }) + if atomic.LoadUint64(&uuidCounter) <= 0 { + return "00000000-0000-0000-0000-000000000000" + } + // first 8 bytes differ, taking a slice of the first 16 bytes + x := atomic.AddUint64(&uuidCounter, 1) + uuid := uuidSeed + binary.LittleEndian.PutUint64(uuid[:8], x) + uuid[6], uuid[9] = uuid[9], uuid[6] + + // RFC4122 v4 + uuid[6] = (uuid[6] & 0x0f) | 0x40 + uuid[8] = uuid[8]&0x3f | 0x80 + + // create UUID representation of the first 128 bits + b := make([]byte, 36) + hex.Encode(b[0:8], uuid[0:4]) + b[8] = '-' + hex.Encode(b[9:13], uuid[4:6]) + b[13] = '-' + hex.Encode(b[14:18], uuid[6:8]) + b[18] = '-' + hex.Encode(b[19:23], uuid[8:10]) + b[23] = '-' + hex.Encode(b[24:], uuid[10:16]) + + return GetString(b) +} + +// FunctionName returns function name +func FunctionName(fn interface{}) string { + t := reflect.ValueOf(fn).Type() + if t.Kind() == reflect.Func { + return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() + } + return t.String() +} + +// GetArgument check if key is in arguments +func GetArgument(arg string) bool { + for i := range os.Args[1:] { + if os.Args[1:][i] == arg { + return true + } + } + return false +} diff --git a/common_test.go b/common_test.go new file mode 100644 index 0000000..740ae43 --- /dev/null +++ b/common_test.go @@ -0,0 +1,66 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "crypto/rand" + "fmt" + "testing" +) + +func Test_Utils_FunctionName(t *testing.T) { + t.Parallel() + AssertEqual(t, "github.com/gofiber/fiber/v2/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) + + AssertEqual(t, "github.com/gofiber/fiber/v2/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) + + var dummyint = 20 + AssertEqual(t, "int", FunctionName(dummyint)) +} + +func Test_Utils_UUID(t *testing.T) { + t.Parallel() + res := UUID() + AssertEqual(t, 36, len(res)) + AssertEqual(t, true, res != "00000000-0000-0000-0000-000000000000") +} + +func Test_Utils_UUID_Concurrency(t *testing.T) { + t.Parallel() + iterations := 10000 + var res string + ch := make(chan string, iterations) + results := make(map[string]string) + for i := 0; i < iterations; i++ { + go func() { + ch <- UUID() + }() + } + for i := 0; i < iterations; i++ { + res = <-ch + results[res] = res + } + AssertEqual(t, iterations, len(results)) +} + +// go test -v -run=^$ -bench=Benchmark_UUID -benchmem -count=2 + +func Benchmark_UUID(b *testing.B) { + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = UUID() + } + AssertEqual(b, 36, len(res)) + }) + b.Run("default", func(b *testing.B) { + rnd := make([]byte, 16) + _, _ = rand.Read(rnd) + for n := 0; n < b.N; n++ { + res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) + } + AssertEqual(b, 36, len(res)) + }) +} diff --git a/convert.go b/convert.go new file mode 100644 index 0000000..ed5b84d --- /dev/null +++ b/convert.go @@ -0,0 +1,104 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "reflect" + "strconv" + "strings" + "unsafe" +) + +// #nosec G103 +// GetString returns a string pointer without allocation +func UnsafeString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// #nosec G103 +// GetBytes returns a byte pointer without allocation +func UnsafeBytes(s string) (bs []byte) { + sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) + bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) + bh.Data = sh.Data + bh.Len = sh.Len + bh.Cap = sh.Len + return +} + +// SafeString copies a string to make it immutable +func SafeString(s string) string { + return string(UnsafeBytes(s)) +} + +// SafeBytes copies a slice to make it immutable +func SafeBytes(b []byte) []byte { + tmp := make([]byte, len(b)) + copy(tmp, b) + return tmp +} + +const ( + uByte = 1 << (10 * iota) + uKilobyte + uMegabyte + uGigabyte + uTerabyte + uPetabyte + uExabyte +) + +// ByteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. +// The unit that results in the smallest number greater than or equal to 1 is always chosen. +func ByteSize(bytes uint64) string { + unit := "" + value := float64(bytes) + switch { + case bytes >= uExabyte: + unit = "EB" + value = value / uExabyte + case bytes >= uPetabyte: + unit = "PB" + value = value / uPetabyte + case bytes >= uTerabyte: + unit = "TB" + value = value / uTerabyte + case bytes >= uGigabyte: + unit = "GB" + value = value / uGigabyte + case bytes >= uMegabyte: + unit = "MB" + value = value / uMegabyte + case bytes >= uKilobyte: + unit = "KB" + value = value / uKilobyte + case bytes >= uByte: + unit = "B" + default: + return "0B" + } + result := strconv.FormatFloat(value, 'f', 1, 64) + result = strings.TrimSuffix(result, ".0") + return result + unit +} + +// Deprecated fn's + +// #nosec G103 +// GetString returns a string pointer without allocation +func GetString(b []byte) string { + return UnsafeString(b) +} + +// #nosec G103 +// GetBytes returns a byte pointer without allocation +func GetBytes(s string) []byte { + return UnsafeBytes(s) +} + +// ImmutableString copies a string to make it immutable +func ImmutableString(s string) string { + return SafeString(s) +} diff --git a/convert_test.go b/convert_test.go new file mode 100644 index 0000000..168d603 --- /dev/null +++ b/convert_test.go @@ -0,0 +1,63 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import "testing" + +func Test_Utils_GetString(t *testing.T) { + t.Parallel() + res := GetString([]byte("Hello, World!")) + AssertEqual(t, "Hello, World!", res) +} + +// go test -v -run=^$ -bench=GetString -benchmem -count=2 + +func Benchmark_GetString(b *testing.B) { + var hello = []byte("Hello, World!") + var res string + b.Run("unsafe", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = GetString(hello) + } + AssertEqual(b, "Hello, World!", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = string(hello) + } + AssertEqual(b, "Hello, World!", res) + }) +} + +func Test_Utils_GetBytes(t *testing.T) { + t.Parallel() + res := GetBytes("Hello, World!") + AssertEqual(t, []byte("Hello, World!"), res) +} + +// go test -v -run=^$ -bench=GetBytes -benchmem -count=4 + +func Benchmark_GetBytes(b *testing.B) { + var hello = "Hello, World!" + var res []byte + b.Run("unsafe", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = GetBytes(hello) + } + AssertEqual(b, []byte("Hello, World!"), res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = []byte(hello) + } + AssertEqual(b, []byte("Hello, World!"), res) + }) +} + +func Test_Utils_ImmutableString(t *testing.T) { + t.Parallel() + res := ImmutableString("Hello, World!") + AssertEqual(t, "Hello, World!", res) +} diff --git a/go.mod b/go.mod deleted file mode 100644 index 682a206..0000000 --- a/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gofiber/utils - -go 1.11 diff --git a/go.sum b/go.sum deleted file mode 100644 index e69de29..0000000 diff --git a/http.go b/http.go new file mode 100644 index 0000000..4584f3c --- /dev/null +++ b/http.go @@ -0,0 +1,212 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +const MIMEOctetStream = "application/octet-stream" + +// GetMIME returns the content-type of a file extension +func GetMIME(extension string) (mime string) { + if len(extension) == 0 { + return mime + } + if extension[0] == '.' { + mime = mimeExtensions[extension[1:]] + } else { + mime = mimeExtensions[extension] + } + if len(mime) == 0 { + return MIMEOctetStream + } + return mime +} + +// limits for HTTP statuscodes +const ( + statusMessageMin = 100 + statusMessageMax = 511 +) + +// StatusMessage returns the correct message for the provided HTTP statuscode +func StatusMessage(status int) string { + if status < statusMessageMin || status > statusMessageMax { + return "" + } + return statusMessage[status] +} + +// HTTP status codes were copied from net/http. +var statusMessage = []string{ + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 103: "Early Hints", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 208: "Already Reported", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 306: "Switch Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", + 421: "Misdirected Request", + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 451: "Unavailable For Legal Reasons", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 510: "Not Extended", + 511: "Network Authentication Required", +} + +// MIME types were copied from https://github.com/nginx/nginx/blob/master/conf/mime.types +var mimeExtensions = map[string]string{ + "html": "text/html", + "htm": "text/html", + "shtml": "text/html", + "css": "text/css", + "gif": "image/gif", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "xml": "application/xml", + "js": "application/javascript", + "atom": "application/atom+xml", + "rss": "application/rss+xml", + "mml": "text/mathml", + "txt": "text/plain", + "jad": "text/vnd.sun.j2me.app-descriptor", + "wml": "text/vnd.wap.wml", + "htc": "text/x-component", + "png": "image/png", + "svg": "image/svg+xml", + "svgz": "image/svg+xml", + "tif": "image/tiff", + "tiff": "image/tiff", + "wbmp": "image/vnd.wap.wbmp", + "webp": "image/webp", + "ico": "image/x-icon", + "jng": "image/x-jng", + "bmp": "image/x-ms-bmp", + "woff": "font/woff", + "woff2": "font/woff2", + "jar": "application/java-archive", + "war": "application/java-archive", + "ear": "application/java-archive", + "json": "application/json", + "hqx": "application/mac-binhex40", + "doc": "application/msword", + "pdf": "application/pdf", + "ps": "application/postscript", + "eps": "application/postscript", + "ai": "application/postscript", + "rtf": "application/rtf", + "m3u8": "application/vnd.apple.mpegurl", + "kml": "application/vnd.google-earth.kml+xml", + "kmz": "application/vnd.google-earth.kmz", + "xls": "application/vnd.ms-excel", + "eot": "application/vnd.ms-fontobject", + "ppt": "application/vnd.ms-powerpoint", + "odg": "application/vnd.oasis.opendocument.graphics", + "odp": "application/vnd.oasis.opendocument.presentation", + "ods": "application/vnd.oasis.opendocument.spreadsheet", + "odt": "application/vnd.oasis.opendocument.text", + "wmlc": "application/vnd.wap.wmlc", + "7z": "application/x-7z-compressed", + "cco": "application/x-cocoa", + "jardiff": "application/x-java-archive-diff", + "jnlp": "application/x-java-jnlp-file", + "run": "application/x-makeself", + "pl": "application/x-perl", + "pm": "application/x-perl", + "prc": "application/x-pilot", + "pdb": "application/x-pilot", + "rar": "application/x-rar-compressed", + "rpm": "application/x-redhat-package-manager", + "sea": "application/x-sea", + "swf": "application/x-shockwave-flash", + "sit": "application/x-stuffit", + "tcl": "application/x-tcl", + "tk": "application/x-tcl", + "der": "application/x-x509-ca-cert", + "pem": "application/x-x509-ca-cert", + "crt": "application/x-x509-ca-cert", + "xpi": "application/x-xpinstall", + "xhtml": "application/xhtml+xml", + "xspf": "application/xspf+xml", + "zip": "application/zip", + "bin": "application/octet-stream", + "exe": "application/octet-stream", + "dll": "application/octet-stream", + "deb": "application/octet-stream", + "dmg": "application/octet-stream", + "iso": "application/octet-stream", + "img": "application/octet-stream", + "msi": "application/octet-stream", + "msp": "application/octet-stream", + "msm": "application/octet-stream", + "mid": "audio/midi", + "midi": "audio/midi", + "kar": "audio/midi", + "mp3": "audio/mpeg", + "ogg": "audio/ogg", + "m4a": "audio/x-m4a", + "ra": "audio/x-realaudio", + "3gpp": "video/3gpp", + "3gp": "video/3gpp", + "ts": "video/mp2t", + "mp4": "video/mp4", + "mpeg": "video/mpeg", + "mpg": "video/mpeg", + "mov": "video/quicktime", + "webm": "video/webm", + "flv": "video/x-flv", + "m4v": "video/x-m4v", + "mng": "video/x-mng", + "asx": "video/x-ms-asf", + "asf": "video/x-ms-asf", + "wmv": "video/x-ms-wmv", + "avi": "video/x-msvideo", +} diff --git a/http_test.go b/http_test.go new file mode 100644 index 0000000..da8dad3 --- /dev/null +++ b/http_test.go @@ -0,0 +1,98 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "mime" + "net/http" + "testing" +) + +func Test_Utils_GetMIME(t *testing.T) { + t.Parallel() + res := GetMIME(".json") + AssertEqual(t, "application/json", res) + + res = GetMIME(".xml") + AssertEqual(t, "application/xml", res) + + res = GetMIME("xml") + AssertEqual(t, "application/xml", res) + + res = GetMIME("unknown") + AssertEqual(t, MIMEOctetStream, res) + // empty case + res = GetMIME("") + AssertEqual(t, "", res) +} + +// go test -v -run=^$ -bench=Benchmark_GetMIME -benchmem -count=2 +func Benchmark_GetMIME(b *testing.B) { + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = GetMIME(".xml") + res = GetMIME(".txt") + res = GetMIME(".png") + res = GetMIME(".exe") + res = GetMIME(".json") + } + AssertEqual(b, "application/json", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = mime.TypeByExtension(".xml") + res = mime.TypeByExtension(".txt") + res = mime.TypeByExtension(".png") + res = mime.TypeByExtension(".exe") + res = mime.TypeByExtension(".json") + } + AssertEqual(b, "application/json", res) + }) +} + +func Test_Utils_StatusMessage(t *testing.T) { + t.Parallel() + res := StatusMessage(204) + AssertEqual(t, "No Content", res) + + res = StatusMessage(404) + AssertEqual(t, "Not Found", res) + + res = StatusMessage(426) + AssertEqual(t, "Upgrade Required", res) + + res = StatusMessage(511) + AssertEqual(t, "Network Authentication Required", res) + + res = StatusMessage(1337) + AssertEqual(t, "", res) + + res = StatusMessage(-1) + AssertEqual(t, "", res) + + res = StatusMessage(0) + AssertEqual(t, "", res) + + res = StatusMessage(600) + AssertEqual(t, "", res) +} + +// go test -run=^$ -bench=Benchmark_StatusMessage -benchmem -count=2 +func Benchmark_StatusMessage(b *testing.B) { + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = StatusMessage(http.StatusNotExtended) + } + AssertEqual(b, "Not Extended", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = http.StatusText(http.StatusNotExtended) + } + AssertEqual(b, "Not Extended", res) + }) +} diff --git a/strings.go b/strings.go new file mode 100644 index 0000000..6657089 --- /dev/null +++ b/strings.go @@ -0,0 +1,62 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +// ToLower is the equivalent of strings.ToLower +func ToLower(b string) string { + var res = make([]byte, len(b)) + copy(res, b) + for i := 0; i < len(res); i++ { + res[i] = toLowerTable[res[i]] + } + + return GetString(res) +} + +// ToUpper is the equivalent of strings.ToUpper +func ToUpper(b string) string { + var res = make([]byte, len(b)) + copy(res, b) + for i := 0; i < len(res); i++ { + res[i] = toUpperTable[res[i]] + } + + return GetString(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] +} diff --git a/strings_test.go b/strings_test.go new file mode 100644 index 0000000..c65142b --- /dev/null +++ b/strings_test.go @@ -0,0 +1,149 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "strings" + "testing" +) + +func Test_Utils_ToUpper(t *testing.T) { + t.Parallel() + res := ToUpper("/my/name/is/:param/*") + AssertEqual(t, "/MY/NAME/IS/:PARAM/*", res) +} + +func Benchmark_ToUpper(b *testing.B) { + var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" + var res string + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToUpper(path) + } + AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.ToUpper(path) + } + AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) + }) +} + +func Test_Utils_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) +} + +func Benchmark_ToLower(b *testing.B) { + var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" + var res string + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = ToLower(path) + } + AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.ToLower(path) + } + AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) + }) +} + +func Test_Utils_TrimRight(t *testing.T) { + t.Parallel() + res := TrimRight("/test//////", '/') + AssertEqual(t, "/test", res) + + res = TrimRight("/test", '/') + AssertEqual(t, "/test", 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_Utils_TrimLeft(t *testing.T) { + t.Parallel() + res := TrimLeft("////test/", '/') + AssertEqual(t, "test/", res) + + res = TrimLeft("test/", '/') + AssertEqual(t, "test/", 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_Utils_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) +} + +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) + }) +} diff --git a/utils.go b/utils.go deleted file mode 100644 index c2b5ddb..0000000 --- a/utils.go +++ /dev/null @@ -1,562 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "encoding/hex" - "fmt" - "log" - "os" - "path/filepath" - "reflect" - "runtime" - "strconv" - "strings" - "sync/atomic" - "testing" - "text/tabwriter" - "unsafe" -) - -const toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" -const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - -// Copyright © 2014, Roger Peppe -// github.com/rogpeppe/fastuuid -// All rights reserved. - -var uuidSeed [24]byte -var uuidCounter uint64 - -func UUID() string { - // Setup seed & counter once - if uuidCounter <= 0 { - if _, err := rand.Read(uuidSeed[:]); err != nil { - return "00000000-0000-0000-0000-000000000000" - } - uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) - } - // first 8 bytes differ, taking a slice of the first 16 bytes - x := atomic.AddUint64(&uuidCounter, 1) - uuid := uuidSeed - binary.LittleEndian.PutUint64(uuid[:8], x) - uuid[6], uuid[9] = uuid[9], uuid[6] - - // RFC4122 v4 - uuid[6] = (uuid[6] & 0x0f) | 0x40 - uuid[8] = uuid[8]&0x3f | 0x80 - - // create UUID representation of the first 128 bits - b := make([]byte, 36) - hex.Encode(b[0:8], uuid[0:4]) - b[8] = '-' - hex.Encode(b[9:13], uuid[4:6]) - b[13] = '-' - hex.Encode(b[14:18], uuid[6:8]) - b[18] = '-' - hex.Encode(b[19:23], uuid[8:10]) - b[23] = '-' - hex.Encode(b[24:], uuid[10:16]) - - return GetString(b) -} - -const ( - uByte = 1 << (10 * iota) - uKilobyte - uMegabyte - uGigabyte - uTerabyte - uPetabyte - uExabyte -) - -// Copyright github.com/cloudfoundry/bytefmt -// ByteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. -// The unit that results in the smallest number greater than or equal to 1 is always chosen. -func ByteSize(bytes uint64) string { - unit := "" - value := float64(bytes) - switch { - case bytes >= uExabyte: - unit = "E" - value = value / uExabyte - case bytes >= uPetabyte: - unit = "P" - value = value / uPetabyte - case bytes >= uTerabyte: - unit = "T" - value = value / uTerabyte - case bytes >= uGigabyte: - unit = "G" - value = value / uGigabyte - case bytes >= uMegabyte: - unit = "M" - value = value / uMegabyte - case bytes >= uKilobyte: - unit = "K" - value = value / uKilobyte - case bytes >= uByte: - unit = "B" - default: - return "0B" - } - result := strconv.FormatFloat(value, 'f', 1, 64) - result = strings.TrimSuffix(result, ".0") - return result + unit -} - -// Returns function name -func FunctionName(fn interface{}) string { - t := reflect.ValueOf(fn).Type() - if t.Kind() == reflect.Func { - return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() - } - return t.String() -} - -// ToLower is the equivalent of strings.ToLower -func ToLower(b string) string { - var res = make([]byte, len(b)) - copy(res, b) - for i := 0; i < len(res); i++ { - res[i] = toLowerTable[res[i]] - } - - return GetString(res) -} - -// ToLowerBytes is the equivalent of bytes.ToLower -func ToLowerBytes(b []byte) []byte { - for i := 0; i < len(b); i++ { - b[i] = toLowerTable[b[i]] - } - return b -} - -// ToUpper is the equivalent of strings.ToUpper -func ToUpper(b string) string { - var res = make([]byte, len(b)) - copy(res, b) - for i := 0; i < len(res); i++ { - res[i] = toUpperTable[res[i]] - } - - return GetString(res) -} - -// ToUpperBytes is the equivalent of bytes.ToUpper -func ToUpperBytes(b []byte) []byte { - for i := 0; i < len(b); i++ { - b[i] = toUpperTable[b[i]] - } - return b -} - -// 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] -} - -// 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] -} - -// 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:] -} - -// 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:] -} - -// 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] -} - -// 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] -} - -// EqualFold the equivalent of bytes.EqualFold -func EqualsFold(b, s []byte) (equals bool) { - n := len(b) - equals = n == len(s) - if equals { - for i := 0; i < n; i++ { - if equals = b[i]|0x20 == s[i]|0x20; !equals { - break - } - } - } - return -} - -// #nosec G103 -// GetString returns a string pointer without allocation -func GetString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - -// #nosec G103 -// GetBytes returns a byte pointer without allocation -func GetBytes(s string) (bs []byte) { - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) - bh.Data = sh.Data - bh.Len = sh.Len - bh.Cap = sh.Len - return -} - -// ImmutableString returns a immutable string with allocation -func ImmutableString(s string) string { - return string(GetBytes(s)) -} - -// AssertEqual checks if values are equal -func AssertEqual(t testing.TB, expected interface{}, actual interface{}, description ...string) { - if reflect.DeepEqual(expected, actual) { - return - } - var aType = "" - var bType = "" - if reflect.ValueOf(expected).IsValid() { - aType = reflect.TypeOf(expected).Name() - } - if reflect.ValueOf(actual).IsValid() { - bType = reflect.TypeOf(actual).Name() - } - - testName := "AssertEqual" - if t != nil { - testName = t.Name() - } - - _, file, line, _ := runtime.Caller(1) - - var buf bytes.Buffer - w := tabwriter.NewWriter(&buf, 0, 0, 5, ' ', 0) - fmt.Fprintf(w, "\nTest:\t%s", testName) - fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) - fmt.Fprintf(w, "\nError:\tNot equal") - fmt.Fprintf(w, "\nExpect:\t%v\t[%s]", expected, aType) - fmt.Fprintf(w, "\nResult:\t%v\t[%s]", actual, bType) - - if len(description) > 0 { - fmt.Fprintf(w, "\nDescription:\t%s", description[0]) - } - - result := "" - if err := w.Flush(); err != nil { - result = err.Error() - } else { - result = buf.String() - } - if t != nil { - t.Fatal(result) - } else { - log.Fatal(result) - } -} - -const MIMEOctetStream = "application/octet-stream" - -// GetMIME returns the content-type of a file extension -func GetMIME(extension string) (mime string) { - if len(extension) == 0 { - return mime - } - if extension[0] == '.' { - mime = mimeExtensions[extension[1:]] - } else { - mime = mimeExtensions[extension] - } - if len(mime) == 0 { - return MIMEOctetStream - } - return mime -} - -// GetTrimmedParam trims the ':' & '?' from a string -func GetTrimmedParam(param string) string { - start := 0 - end := len(param) - - if param[start] != ':' { // is not a param - return param - } - start++ - if param[end-1] == '?' { // is ? - end-- - } - - return param[start:end] -} - -// GetCharPos ... -func GetCharPos(s string, char byte, matchCount int) int { - if matchCount == 0 { - matchCount = 1 - } - endPos, pos := 0, -2 - for matchCount > 0 && pos != -1 { - if pos > -1 { - s = s[pos+1:] - endPos++ - } - pos = strings.IndexByte(s, char) - endPos += pos - matchCount-- - } - return endPos -} - -// GetArgument check if key is in arguments -func GetArgument(arg string) bool { - for i := range os.Args[1:] { - if os.Args[1:][i] == arg { - return true - } - } - return false -} - -// limit for the access -const ( - statusMessageMin = 100 - statusMessageMax = 511 -) - -// StatusMessage returns the correct message for the provided HTTP statuscode -func StatusMessage(status int) string { - if status < statusMessageMin || status > statusMessageMax { - return "" - } - return statusMessage[status] -} - -// HTTP status codes were copied from net/http. -var statusMessage = []string{ - 100: "Continue", - 101: "Switching Protocols", - 102: "Processing", - 103: "Early Hints", - 200: "OK", - 201: "Created", - 202: "Accepted", - 203: "Non-Authoritative Information", - 204: "No Content", - 205: "Reset Content", - 206: "Partial Content", - 207: "Multi-Status", - 208: "Already Reported", - 226: "IM Used", - 300: "Multiple Choices", - 301: "Moved Permanently", - 302: "Found", - 303: "See Other", - 304: "Not Modified", - 305: "Use Proxy", - 306: "Switch Proxy", - 307: "Temporary Redirect", - 308: "Permanent Redirect", - 400: "Bad Request", - 401: "Unauthorized", - 402: "Payment Required", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Not Acceptable", - 407: "Proxy Authentication Required", - 408: "Request Timeout", - 409: "Conflict", - 410: "Gone", - 411: "Length Required", - 412: "Precondition Failed", - 413: "Request Entity Too Large", - 414: "Request URI Too Long", - 415: "Unsupported Media Type", - 416: "Requested Range Not Satisfiable", - 417: "Expectation Failed", - 418: "I'm a teapot", - 421: "Misdirected Request", - 422: "Unprocessable Entity", - 423: "Locked", - 424: "Failed Dependency", - 426: "Upgrade Required", - 428: "Precondition Required", - 429: "Too Many Requests", - 431: "Request Header Fields Too Large", - 451: "Unavailable For Legal Reasons", - 500: "Internal Server Error", - 501: "Not Implemented", - 502: "Bad Gateway", - 503: "Service Unavailable", - 504: "Gateway Timeout", - 505: "HTTP Version Not Supported", - 506: "Variant Also Negotiates", - 507: "Insufficient Storage", - 508: "Loop Detected", - 510: "Not Extended", - 511: "Network Authentication Required", -} - -// MIME types were copied from https://github.com/nginx/nginx/blob/master/conf/mime.types -var mimeExtensions = map[string]string{ - "html": "text/html", - "htm": "text/html", - "shtml": "text/html", - "css": "text/css", - "gif": "image/gif", - "jpeg": "image/jpeg", - "jpg": "image/jpeg", - "xml": "application/xml", - "js": "application/javascript", - "atom": "application/atom+xml", - "rss": "application/rss+xml", - "mml": "text/mathml", - "txt": "text/plain", - "jad": "text/vnd.sun.j2me.app-descriptor", - "wml": "text/vnd.wap.wml", - "htc": "text/x-component", - "png": "image/png", - "svg": "image/svg+xml", - "svgz": "image/svg+xml", - "tif": "image/tiff", - "tiff": "image/tiff", - "wbmp": "image/vnd.wap.wbmp", - "webp": "image/webp", - "ico": "image/x-icon", - "jng": "image/x-jng", - "bmp": "image/x-ms-bmp", - "woff": "font/woff", - "woff2": "font/woff2", - "jar": "application/java-archive", - "war": "application/java-archive", - "ear": "application/java-archive", - "json": "application/json", - "hqx": "application/mac-binhex40", - "doc": "application/msword", - "pdf": "application/pdf", - "ps": "application/postscript", - "eps": "application/postscript", - "ai": "application/postscript", - "rtf": "application/rtf", - "m3u8": "application/vnd.apple.mpegurl", - "kml": "application/vnd.google-earth.kml+xml", - "kmz": "application/vnd.google-earth.kmz", - "xls": "application/vnd.ms-excel", - "eot": "application/vnd.ms-fontobject", - "ppt": "application/vnd.ms-powerpoint", - "odg": "application/vnd.oasis.opendocument.graphics", - "odp": "application/vnd.oasis.opendocument.presentation", - "ods": "application/vnd.oasis.opendocument.spreadsheet", - "odt": "application/vnd.oasis.opendocument.text", - "wmlc": "application/vnd.wap.wmlc", - "7z": "application/x-7z-compressed", - "cco": "application/x-cocoa", - "jardiff": "application/x-java-archive-diff", - "jnlp": "application/x-java-jnlp-file", - "run": "application/x-makeself", - "pl": "application/x-perl", - "pm": "application/x-perl", - "prc": "application/x-pilot", - "pdb": "application/x-pilot", - "rar": "application/x-rar-compressed", - "rpm": "application/x-redhat-package-manager", - "sea": "application/x-sea", - "swf": "application/x-shockwave-flash", - "sit": "application/x-stuffit", - "tcl": "application/x-tcl", - "tk": "application/x-tcl", - "der": "application/x-x509-ca-cert", - "pem": "application/x-x509-ca-cert", - "crt": "application/x-x509-ca-cert", - "xpi": "application/x-xpinstall", - "xhtml": "application/xhtml+xml", - "xspf": "application/xspf+xml", - "zip": "application/zip", - "bin": "application/octet-stream", - "exe": "application/octet-stream", - "dll": "application/octet-stream", - "deb": "application/octet-stream", - "dmg": "application/octet-stream", - "iso": "application/octet-stream", - "img": "application/octet-stream", - "msi": "application/octet-stream", - "msp": "application/octet-stream", - "msm": "application/octet-stream", - "mid": "audio/midi", - "midi": "audio/midi", - "kar": "audio/midi", - "mp3": "audio/mpeg", - "ogg": "audio/ogg", - "m4a": "audio/x-m4a", - "ra": "audio/x-realaudio", - "3gpp": "video/3gpp", - "3gp": "video/3gpp", - "ts": "video/mp2t", - "mp4": "video/mp4", - "mpeg": "video/mpeg", - "mpg": "video/mpeg", - "mov": "video/quicktime", - "webm": "video/webm", - "flv": "video/x-flv", - "m4v": "video/x-m4v", - "mng": "video/x-mng", - "asx": "video/x-ms-asf", - "asf": "video/x-ms-asf", - "wmv": "video/x-ms-wmv", - "avi": "video/x-msvideo", -} diff --git a/utils_bench_test.go b/utils_bench_test.go deleted file mode 100644 index 7a6431e..0000000 --- a/utils_bench_test.go +++ /dev/null @@ -1,304 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "bytes" - "crypto/rand" - "fmt" - "mime" - "net/http" - "strings" - "testing" -) - -// go test -v -run=^$ -bench=Benchmark_UUID -benchmem -count=2 - -func Benchmark_UUID(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = UUID() - } - AssertEqual(b, 36, len(res)) - }) - b.Run("default", func(b *testing.B) { - rnd := make([]byte, 16) - _, _ = rand.Read(rnd) - for n := 0; n < b.N; n++ { - res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) - } - AssertEqual(b, 36, len(res)) - }) -} - -// go test -v -run=^$ -bench=GetString -benchmem -count=2 - -func Benchmark_GetString(b *testing.B) { - var hello = []byte("Hello, World!") - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = GetString(hello) - } - AssertEqual(b, "Hello, World!", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = string(hello) - } - AssertEqual(b, "Hello, World!", res) - }) -} - -// go test -v -cpu 2 -run=^$ -bench=GetBytes -benchmem -count=4 - -func Benchmark_GetBytes(b *testing.B) { - var hello = "Hello, World!" - var res []byte - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = GetBytes(hello) - } - AssertEqual(b, []byte("Hello, World!"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = []byte(hello) - } - AssertEqual(b, []byte("Hello, World!"), res) - }) -} - -// go test -v -run=^$ -bench=Benchmark_GetMIME -benchmem -count=2 -func Benchmark_GetMIME(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = GetMIME(".xml") - res = GetMIME(".txt") - res = GetMIME(".png") - res = GetMIME(".exe") - res = GetMIME(".json") - } - AssertEqual(b, "application/json", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = mime.TypeByExtension(".xml") - res = mime.TypeByExtension(".txt") - res = mime.TypeByExtension(".png") - res = mime.TypeByExtension(".exe") - res = mime.TypeByExtension(".json") - } - AssertEqual(b, "application/json", res) - }) -} - -// go test -v -run=^$ -bench=Benchmark_StatusMessage -benchmem -count=4 -func Benchmark_StatusMessage(b *testing.B) { - var res string - for n := 0; n < b.N; n++ { - res = StatusMessage(http.StatusNotExtended) - } - AssertEqual(b, "Not Extended", res) -} - -func Benchmark_ToLower(b *testing.B) { - var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToLower(path) - } - AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.ToLower(path) - } - AssertEqual(b, "/repos/gofiber/fiber/issues/187643/comments", res) - }) -} - -func Benchmark_ToLowerBytes(b *testing.B) { - var path = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToLowerBytes(path) - } - AssertEqual(b, bytes.EqualFold(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.ToLower(path) - } - AssertEqual(b, bytes.EqualFold(GetBytes("/repos/gofiber/fiber/issues/187643/comments"), res), true) - }) -} - -func Benchmark_ToUpper(b *testing.B) { - var path = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts" - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToUpper(path) - } - AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.ToUpper(path) - } - AssertEqual(b, "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS", res) - }) -} - -func Benchmark_ToUpperBytes(b *testing.B) { - var path = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToUpperBytes(path) - } - AssertEqual(b, bytes.EqualFold(GetBytes("/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS"), res), true) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.ToUpper(path) - } - AssertEqual(b, bytes.EqualFold(GetBytes("/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS"), res), true) - }) -} - -func Benchmark_EqualFolds(b *testing.B) { - var left = []byte("/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts") - var right = []byte("/RePos/goFiber/Fiber/issues/187643/COMMENTS") - var res bool - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = EqualsFold(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 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) - }) -} - -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_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 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 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 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) - }) -} diff --git a/utils_test.go b/utils_test.go deleted file mode 100644 index ac2f846..0000000 --- a/utils_test.go +++ /dev/null @@ -1,237 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "bytes" - "testing" -) - -func Test_Utils_FunctionName(t *testing.T) { - t.Parallel() - AssertEqual(t, "github.com/gofiber/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) - - AssertEqual(t, "github.com/gofiber/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) - - var dummyint = 20 - AssertEqual(t, "int", FunctionName(dummyint)) -} - -func Test_Utils_UUID(t *testing.T) { - t.Parallel() - res := UUID() - AssertEqual(t, 36, len(res)) -} - -func Test_Utils_ToUpper(t *testing.T) { - t.Parallel() - res := ToUpper("/my/name/is/:param/*") - AssertEqual(t, "/MY/NAME/IS/:PARAM/*", res) -} - -func Test_Utils_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) -} - -func Test_Utils_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)) -} - -func Test_Utils_EqualsFold(t *testing.T) { - t.Parallel() - res := EqualsFold([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualsFold([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*")) - AssertEqual(t, true, res) - res = EqualsFold([]byte("/my2/name/is/:param/*"), []byte("/my2/name")) - AssertEqual(t, false, res) - res = EqualsFold([]byte("/dddddd"), []byte("eeeeee")) - AssertEqual(t, false, res) - res = EqualsFold([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualsFold([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*")) - AssertEqual(t, true, res) -} - -func Test_Utils_TrimRight(t *testing.T) { - t.Parallel() - res := TrimRight("/test//////", '/') - AssertEqual(t, "/test", res) - - res = TrimRight("/test", '/') - AssertEqual(t, "/test", res) -} - -func Test_Utils_TrimLeft(t *testing.T) { - t.Parallel() - res := TrimLeft("////test/", '/') - AssertEqual(t, "test/", res) - - res = TrimLeft("test/", '/') - AssertEqual(t, "test/", res) -} -func Test_Utils_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) -} - -func Test_Utils_TrimRightBytes(t *testing.T) { - t.Parallel() - res := TrimRightBytes([]byte("/test//////"), '/') - AssertEqual(t, []byte("/test"), res) - - res = TrimRightBytes([]byte("/test"), '/') - AssertEqual(t, []byte("/test"), res) -} - -func Test_Utils_TrimLeftBytes(t *testing.T) { - t.Parallel() - res := TrimLeftBytes([]byte("////test/"), '/') - AssertEqual(t, []byte("test/"), res) - - res = TrimLeftBytes([]byte("test/"), '/') - AssertEqual(t, []byte("test/"), res) -} -func Test_Utils_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) -} - -// func Test_Utils_getArgument(t *testing.T) { -// // TODO -// } - -// func Test_Utils_parseTokenList(t *testing.T) { -// // TODO -// } - -func Test_Utils_GetCharPos(t *testing.T) { - t.Parallel() - res := GetCharPos("/foo/bar/foobar/test", '/', 3) - AssertEqual(t, 8, res) - res = GetCharPos("foo/bar/foobar/test", '/', 3) - AssertEqual(t, 14, res) - res = GetCharPos("foo/bar/foobar/test", '/', 1) - AssertEqual(t, 3, res) - res = GetCharPos("foo/bar/foobar/test", 'f', 2) - AssertEqual(t, 8, res) - res = GetCharPos("foo/bar/foobar/test", 'f', 0) - AssertEqual(t, 0, res) -} - -func Test_Utils_GetTrimmedParam(t *testing.T) { - t.Parallel() - res := GetTrimmedParam("*") - AssertEqual(t, "*", res) - res = GetTrimmedParam(":param") - AssertEqual(t, "param", res) - res = GetTrimmedParam(":param1?") - AssertEqual(t, "param1", res) - res = GetTrimmedParam("noParam") - AssertEqual(t, "noParam", res) -} - -func Test_Utils_GetString(t *testing.T) { - t.Parallel() - res := GetString([]byte("Hello, World!")) - AssertEqual(t, "Hello, World!", res) -} - -func Test_Utils_GetBytes(t *testing.T) { - t.Parallel() - res := GetBytes("Hello, World!") - AssertEqual(t, []byte("Hello, World!"), res) -} - -func Test_Utils_ImmutableString(t *testing.T) { - t.Parallel() - res := ImmutableString("Hello, World!") - AssertEqual(t, "Hello, World!", res) -} - -func Test_Utils_GetMIME(t *testing.T) { - t.Parallel() - res := GetMIME(".json") - AssertEqual(t, "application/json", res) - - res = GetMIME(".xml") - AssertEqual(t, "application/xml", res) - - res = GetMIME("xml") - AssertEqual(t, "application/xml", res) - - res = GetMIME("unknown") - AssertEqual(t, MIMEOctetStream, res) - // empty case - res = GetMIME("") - AssertEqual(t, "", res) -} - -func Test_Utils_StatusMessage(t *testing.T) { - t.Parallel() - res := StatusMessage(204) - AssertEqual(t, "No Content", res) - - res = StatusMessage(404) - AssertEqual(t, "Not Found", res) - - res = StatusMessage(426) - AssertEqual(t, "Upgrade Required", res) - - res = StatusMessage(511) - AssertEqual(t, "Network Authentication Required", res) - - res = StatusMessage(1337) - AssertEqual(t, "", res) - - res = StatusMessage(-1) - AssertEqual(t, "", res) - - res = StatusMessage(0) - AssertEqual(t, "", res) - - res = StatusMessage(600) - AssertEqual(t, "", res) -} - -func Test_Utils_AssertEqual(t *testing.T) { - t.Parallel() - AssertEqual(nil, []string{}, []string{}) - AssertEqual(t, []string{}, []string{}) -} From 475d94b611495b1faf6726abc844af3c2eaa36c4 Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Sat, 31 Oct 2020 10:37:43 +0100 Subject: [PATCH 2/5] Update common_test.go --- common_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common_test.go b/common_test.go index 740ae43..defdd28 100644 --- a/common_test.go +++ b/common_test.go @@ -12,9 +12,9 @@ import ( func Test_Utils_FunctionName(t *testing.T) { t.Parallel() - AssertEqual(t, "github.com/gofiber/fiber/v2/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) + AssertEqual(t, "_/home/runner/work/utils/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) - AssertEqual(t, "github.com/gofiber/fiber/v2/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) + AssertEqual(t, "_/home/runner/work/utils/v2/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) var dummyint = 20 AssertEqual(t, "int", FunctionName(dummyint)) From b6c00c0b60d23741a9bd9fe7fdb6e5b63e9e44dc Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Sat, 31 Oct 2020 10:40:04 +0100 Subject: [PATCH 3/5] Update common_test.go --- common_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common_test.go b/common_test.go index defdd28..64b038c 100644 --- a/common_test.go +++ b/common_test.go @@ -14,7 +14,7 @@ func Test_Utils_FunctionName(t *testing.T) { t.Parallel() AssertEqual(t, "_/home/runner/work/utils/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) - AssertEqual(t, "_/home/runner/work/utils/v2/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) + AssertEqual(t, "_/home/runner/work/utils/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) var dummyint = 20 AssertEqual(t, "int", FunctionName(dummyint)) From 40b100086447b158b8bc3eea7b83f2d99ecad2cc Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Sat, 31 Oct 2020 10:40:39 +0100 Subject: [PATCH 4/5] Create go.mod --- go.mod | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..098520f --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/gofiber/utils + +go 1.14 From d470af739813792b9f1601a434337f0370397fdf Mon Sep 17 00:00:00 2001 From: Fenny <25108519+Fenny@users.noreply.github.com> Date: Sat, 31 Oct 2020 10:52:06 +0100 Subject: [PATCH 5/5] Update common_test.go --- common_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common_test.go b/common_test.go index 64b038c..607bb53 100644 --- a/common_test.go +++ b/common_test.go @@ -12,9 +12,9 @@ import ( func Test_Utils_FunctionName(t *testing.T) { t.Parallel() - AssertEqual(t, "_/home/runner/work/utils/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) + AssertEqual(t, "github.com/gofiber/utils.Test_Utils_UUID", FunctionName(Test_Utils_UUID)) - AssertEqual(t, "_/home/runner/work/utils/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) + AssertEqual(t, "github.com/gofiber/utils.Test_Utils_FunctionName.func1", FunctionName(func() {})) var dummyint = 20 AssertEqual(t, "int", FunctionName(dummyint))