Adding upstream version 2.52.6.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
a960158181
commit
6d002e9543
441 changed files with 95392 additions and 0 deletions
90
utils/README.md
Normal file
90
utils/README.md
Normal file
|
@ -0,0 +1,90 @@
|
|||
A collection of common functions but with better performance, less allocations and no dependencies created for [Fiber](https://github.com/gofiber/fiber).
|
||||
|
||||
```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
|
||||
```
|
68
utils/assertions.go
Normal file
68
utils/assertions.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
// ⚡️ 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(tb testing.TB, expected, actual interface{}, description ...string) { //nolint:thelper // TODO: Verify if tb can be nil
|
||||
if tb != nil {
|
||||
tb.Helper()
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(expected, actual) {
|
||||
return
|
||||
}
|
||||
|
||||
aType := "<nil>"
|
||||
bType := "<nil>"
|
||||
|
||||
if expected != nil {
|
||||
aType = reflect.TypeOf(expected).String()
|
||||
}
|
||||
if actual != nil {
|
||||
bType = reflect.TypeOf(actual).String()
|
||||
}
|
||||
|
||||
testName := "AssertEqual"
|
||||
if tb != nil {
|
||||
testName = tb.Name()
|
||||
}
|
||||
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
|
||||
var buf bytes.Buffer
|
||||
const pad = 5
|
||||
w := tabwriter.NewWriter(&buf, 0, 0, pad, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "\nTest:\t%s", testName)
|
||||
_, _ = fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line)
|
||||
if len(description) > 0 {
|
||||
_, _ = fmt.Fprintf(w, "\nDescription:\t%s", description[0])
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType)
|
||||
_, _ = fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType)
|
||||
|
||||
var result string
|
||||
if err := w.Flush(); err != nil {
|
||||
result = err.Error()
|
||||
} else {
|
||||
result = buf.String()
|
||||
}
|
||||
|
||||
if tb != nil {
|
||||
tb.Fatal(result)
|
||||
} else {
|
||||
log.Fatal(result) //nolint:revive // tb might be nil, so we need a fallback
|
||||
}
|
||||
}
|
15
utils/assertions_test.go
Normal file
15
utils/assertions_test.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// ⚡️ 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_AssertEqual(t *testing.T) {
|
||||
t.Parallel()
|
||||
AssertEqual(nil, []string{}, []string{})
|
||||
AssertEqual(t, []string{}, []string{})
|
||||
}
|
69
utils/bytes.go
Normal file
69
utils/bytes.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
// ⚡️ 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 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]]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
218
utils/bytes_test.go
Normal file
218
utils/bytes_test.go
Normal file
|
@ -0,0 +1,218 @@
|
|||
// ⚡️ 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_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) {
|
||||
path := []byte(largeStr)
|
||||
want := []byte(lowerStr)
|
||||
var res []byte
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = ToLowerBytes(path)
|
||||
}
|
||||
AssertEqual(b, bytes.Equal(want, res), true)
|
||||
})
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
func Benchmark_ToUpperBytes(b *testing.B) {
|
||||
path := []byte(largeStr)
|
||||
want := []byte(upperStr)
|
||||
var res []byte
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = ToUpperBytes(path)
|
||||
}
|
||||
AssertEqual(b, bytes.Equal(want, res), true)
|
||||
})
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
160
utils/common.go
Normal file
160
utils/common.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
// ⚡️ 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"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unicode"
|
||||
|
||||
googleuuid "github.com/google/uuid"
|
||||
)
|
||||
|
||||
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"
|
||||
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.
|
||||
|
||||
const (
|
||||
emptyUUID = "00000000-0000-0000-0000-000000000000"
|
||||
)
|
||||
|
||||
var (
|
||||
uuidSeed [24]byte
|
||||
uuidCounter uint64
|
||||
uuidSetup sync.Once
|
||||
unitsSlice = []byte("kmgtp")
|
||||
)
|
||||
|
||||
// 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 emptyUUID
|
||||
}
|
||||
// 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 UnsafeString(b)
|
||||
}
|
||||
|
||||
// UUIDv4 returns a Random (Version 4) UUID.
|
||||
// The strength of the UUIDs is based on the strength of the crypto/rand package.
|
||||
func UUIDv4() string {
|
||||
token, err := googleuuid.NewRandom()
|
||||
if err != nil {
|
||||
return UUID()
|
||||
}
|
||||
return token.String()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// IncrementIPRange Find available next IP address
|
||||
func IncrementIPRange(ip net.IP) {
|
||||
for j := len(ip) - 1; j >= 0; j-- {
|
||||
ip[j]++
|
||||
if ip[j] > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertToBytes returns integer size of bytes from human-readable string, ex. 42kb, 42M
|
||||
// Returns 0 if string is unrecognized
|
||||
func ConvertToBytes(humanReadableString string) int {
|
||||
strLen := len(humanReadableString)
|
||||
if strLen == 0 {
|
||||
return 0
|
||||
}
|
||||
var unitPrefixPos, lastNumberPos int
|
||||
// loop the string
|
||||
for i := strLen - 1; i >= 0; i-- {
|
||||
// check if the char is a number
|
||||
if unicode.IsDigit(rune(humanReadableString[i])) {
|
||||
lastNumberPos = i
|
||||
break
|
||||
} else if humanReadableString[i] != ' ' {
|
||||
unitPrefixPos = i
|
||||
}
|
||||
}
|
||||
|
||||
if lastNumberPos < 0 {
|
||||
return 0
|
||||
}
|
||||
// fetch the number part and parse it to float
|
||||
size, err := strconv.ParseFloat(humanReadableString[:lastNumberPos+1], 64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// check the multiplier from the string and use it
|
||||
if unitPrefixPos > 0 {
|
||||
// convert multiplier char to lowercase and check if exists in units slice
|
||||
index := bytes.IndexByte(unitsSlice, toLowerTable[humanReadableString[unitPrefixPos]])
|
||||
if index != -1 {
|
||||
const bytesPerKB = 1000
|
||||
size *= math.Pow(bytesPerKB, float64(index+1))
|
||||
}
|
||||
}
|
||||
|
||||
return int(size)
|
||||
}
|
127
utils/common_test.go
Normal file
127
utils/common_test.go
Normal file
|
@ -0,0 +1,127 @@
|
|||
// ⚡️ 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_FunctionName(t *testing.T) {
|
||||
t.Parallel()
|
||||
AssertEqual(t, "github.com/gofiber/fiber/v2/utils.Test_UUID", FunctionName(Test_UUID))
|
||||
|
||||
AssertEqual(t, "github.com/gofiber/fiber/v2/utils.Test_FunctionName.func1", FunctionName(func() {}))
|
||||
|
||||
dummyint := 20
|
||||
AssertEqual(t, "int", FunctionName(dummyint))
|
||||
}
|
||||
|
||||
func Test_UUID(t *testing.T) {
|
||||
t.Parallel()
|
||||
res := UUID()
|
||||
AssertEqual(t, 36, len(res))
|
||||
AssertEqual(t, true, res != emptyUUID)
|
||||
}
|
||||
|
||||
func Test_UUID_Concurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
iterations := 1000
|
||||
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))
|
||||
}
|
||||
|
||||
func Test_UUIDv4(t *testing.T) {
|
||||
t.Parallel()
|
||||
res := UUIDv4()
|
||||
AssertEqual(t, 36, len(res))
|
||||
AssertEqual(t, true, res != emptyUUID)
|
||||
}
|
||||
|
||||
func Test_UUIDv4_Concurrency(t *testing.T) {
|
||||
t.Parallel()
|
||||
iterations := 1000
|
||||
var res string
|
||||
ch := make(chan string, iterations)
|
||||
results := make(map[string]string)
|
||||
for i := 0; i < iterations; i++ {
|
||||
go func() {
|
||||
ch <- UUIDv4()
|
||||
}()
|
||||
}
|
||||
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)
|
||||
_, err := rand.Read(rnd)
|
||||
AssertEqual(b, nil, err)
|
||||
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))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ConvertToBytes(t *testing.T) {
|
||||
t.Parallel()
|
||||
AssertEqual(t, 0, ConvertToBytes(""))
|
||||
AssertEqual(t, 42, ConvertToBytes("42"))
|
||||
AssertEqual(t, 42, ConvertToBytes("42b"))
|
||||
AssertEqual(t, 42, ConvertToBytes("42B"))
|
||||
AssertEqual(t, 42, ConvertToBytes("42 b"))
|
||||
AssertEqual(t, 42, ConvertToBytes("42 B"))
|
||||
|
||||
AssertEqual(t, 42*1000, ConvertToBytes("42k"))
|
||||
AssertEqual(t, 42*1000, ConvertToBytes("42K"))
|
||||
AssertEqual(t, 42*1000, ConvertToBytes("42kb"))
|
||||
AssertEqual(t, 42*1000, ConvertToBytes("42KB"))
|
||||
AssertEqual(t, 42*1000, ConvertToBytes("42 kb"))
|
||||
AssertEqual(t, 42*1000, ConvertToBytes("42 KB"))
|
||||
|
||||
AssertEqual(t, 42*1000000, ConvertToBytes("42M"))
|
||||
AssertEqual(t, int(42.5*1000000), ConvertToBytes("42.5MB"))
|
||||
AssertEqual(t, 42*1000000000, ConvertToBytes("42G"))
|
||||
|
||||
AssertEqual(t, 0, ConvertToBytes("string"))
|
||||
AssertEqual(t, 0, ConvertToBytes("MB"))
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=Benchmark_ConvertToBytes -benchmem -count=2
|
||||
func Benchmark_ConvertToBytes(b *testing.B) {
|
||||
var res int
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = ConvertToBytes("42B")
|
||||
}
|
||||
AssertEqual(b, 42, res)
|
||||
})
|
||||
}
|
117
utils/convert.go
Normal file
117
utils/convert.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
// ⚡️ 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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CopyString copies a string to make it immutable
|
||||
func CopyString(s string) string {
|
||||
return string(UnsafeBytes(s))
|
||||
}
|
||||
|
||||
// CopyBytes copies a slice to make it immutable
|
||||
func CopyBytes(b []byte) []byte {
|
||||
tmp := make([]byte, len(b))
|
||||
copy(tmp, b)
|
||||
return tmp
|
||||
}
|
||||
|
||||
const (
|
||||
uByte = 1 << (10 * iota) // 1 << 10 == 1024
|
||||
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 /= uExabyte
|
||||
case bytes >= uPetabyte:
|
||||
unit = "PB"
|
||||
value /= uPetabyte
|
||||
case bytes >= uTerabyte:
|
||||
unit = "TB"
|
||||
value /= uTerabyte
|
||||
case bytes >= uGigabyte:
|
||||
unit = "GB"
|
||||
value /= uGigabyte
|
||||
case bytes >= uMegabyte:
|
||||
unit = "MB"
|
||||
value /= uMegabyte
|
||||
case bytes >= uKilobyte:
|
||||
unit = "KB"
|
||||
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
|
||||
}
|
||||
|
||||
// ToString Change arg to string
|
||||
func ToString(arg interface{}, timeFormat ...string) string {
|
||||
tmp := reflect.Indirect(reflect.ValueOf(arg)).Interface()
|
||||
switch v := tmp.(type) {
|
||||
case int:
|
||||
return strconv.Itoa(v)
|
||||
case int8:
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
case int16:
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
case int32:
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
case int64:
|
||||
return strconv.FormatInt(v, 10)
|
||||
case uint:
|
||||
return strconv.Itoa(int(v))
|
||||
case uint8:
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
case uint16:
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
case uint32:
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
case uint64:
|
||||
return strconv.FormatInt(int64(v), 10)
|
||||
case string:
|
||||
return v
|
||||
case []byte:
|
||||
return string(v)
|
||||
case bool:
|
||||
return strconv.FormatBool(v)
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(v), 'f', -1, 32)
|
||||
case float64:
|
||||
return strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case time.Time:
|
||||
if len(timeFormat) > 0 {
|
||||
return v.Format(timeFormat[0])
|
||||
}
|
||||
return v.Format("2006-01-02 15:04:05")
|
||||
case reflect.Value:
|
||||
return ToString(v.Interface(), timeFormat...)
|
||||
case fmt.Stringer:
|
||||
return v.String()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
12
utils/convert_b2s_new.go
Normal file
12
utils/convert_b2s_new.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
//go:build go1.20
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// UnsafeString returns a string pointer without allocation
|
||||
func UnsafeString(b []byte) string {
|
||||
return unsafe.String(unsafe.SliceData(b), len(b))
|
||||
}
|
14
utils/convert_b2s_old.go
Normal file
14
utils/convert_b2s_old.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
//go:build !go1.20
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// UnsafeString returns a string pointer without allocation
|
||||
//
|
||||
//nolint:gosec // unsafe is used for better performance here
|
||||
func UnsafeString(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
12
utils/convert_s2b_new.go
Normal file
12
utils/convert_s2b_new.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
//go:build go1.20
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// UnsafeBytes returns a byte pointer without allocation.
|
||||
func UnsafeBytes(s string) []byte {
|
||||
return unsafe.Slice(unsafe.StringData(s), len(s))
|
||||
}
|
24
utils/convert_s2b_old.go
Normal file
24
utils/convert_s2b_old.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
//go:build !go1.20
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112)
|
||||
|
||||
// UnsafeBytes returns a byte pointer without allocation.
|
||||
// String length shouldn't be more than 2147418112.
|
||||
//
|
||||
//nolint:gosec // unsafe is used for better performance here
|
||||
func UnsafeBytes(s string) []byte {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (*[MaxStringLen]byte)(unsafe.Pointer(
|
||||
(*reflect.StringHeader)(unsafe.Pointer(&s)).Data),
|
||||
)[:len(s):len(s)]
|
||||
}
|
83
utils/convert_test.go
Normal file
83
utils/convert_test.go
Normal file
|
@ -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 (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_UnsafeString(t *testing.T) {
|
||||
t.Parallel()
|
||||
res := UnsafeString([]byte("Hello, World!"))
|
||||
AssertEqual(t, "Hello, World!", res)
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=UnsafeString -benchmem -count=2
|
||||
|
||||
func Benchmark_UnsafeString(b *testing.B) {
|
||||
hello := []byte("Hello, World!")
|
||||
var res string
|
||||
b.Run("unsafe", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = UnsafeString(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_UnsafeBytes(t *testing.T) {
|
||||
t.Parallel()
|
||||
res := UnsafeBytes("Hello, World!")
|
||||
AssertEqual(t, []byte("Hello, World!"), res)
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=UnsafeBytes -benchmem -count=4
|
||||
|
||||
func Benchmark_UnsafeBytes(b *testing.B) {
|
||||
hello := "Hello, World!"
|
||||
var res []byte
|
||||
b.Run("unsafe", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = UnsafeBytes(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_CopyString(t *testing.T) {
|
||||
t.Parallel()
|
||||
res := CopyString("Hello, World!")
|
||||
AssertEqual(t, "Hello, World!", res)
|
||||
}
|
||||
|
||||
func Test_ToString(t *testing.T) {
|
||||
t.Parallel()
|
||||
res := ToString([]byte("Hello, World!"))
|
||||
AssertEqual(t, "Hello, World!", res)
|
||||
res = ToString(true)
|
||||
AssertEqual(t, "true", res)
|
||||
res = ToString(uint(100))
|
||||
AssertEqual(t, "100", res)
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=ToString -benchmem -count=2
|
||||
func Benchmark_ToString(b *testing.B) {
|
||||
hello := []byte("Hello, World!")
|
||||
for n := 0; n < b.N; n++ {
|
||||
ToString(hello)
|
||||
}
|
||||
}
|
16
utils/deprecated.go
Normal file
16
utils/deprecated.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package utils
|
||||
|
||||
// Deprecated: Please use UnsafeString instead
|
||||
func GetString(b []byte) string {
|
||||
return UnsafeString(b)
|
||||
}
|
||||
|
||||
// Deprecated: Please use UnsafeBytes instead
|
||||
func GetBytes(s string) []byte {
|
||||
return UnsafeBytes(s)
|
||||
}
|
||||
|
||||
// Deprecated: Please use CopyString instead
|
||||
func ImmutableString(s string) string {
|
||||
return CopyString(s)
|
||||
}
|
267
utils/http.go
Normal file
267
utils/http.go
Normal file
|
@ -0,0 +1,267 @@
|
|||
// ⚡️ 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"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const MIMEOctetStream = "application/octet-stream"
|
||||
|
||||
// GetMIME returns the content-type of a file extension
|
||||
func GetMIME(extension string) string {
|
||||
if len(extension) == 0 {
|
||||
return ""
|
||||
}
|
||||
var foundMime string
|
||||
if extension[0] == '.' {
|
||||
foundMime = mimeExtensions[extension[1:]]
|
||||
} else {
|
||||
foundMime = mimeExtensions[extension]
|
||||
}
|
||||
|
||||
if len(foundMime) == 0 {
|
||||
if extension[0] != '.' {
|
||||
foundMime = mime.TypeByExtension("." + extension)
|
||||
} else {
|
||||
foundMime = mime.TypeByExtension(extension)
|
||||
}
|
||||
|
||||
if foundMime == "" {
|
||||
return MIMEOctetStream
|
||||
}
|
||||
}
|
||||
return foundMime
|
||||
}
|
||||
|
||||
// ParseVendorSpecificContentType check if content type is vendor specific and
|
||||
// if it is parsable to any known types. If its not vendor specific then returns
|
||||
// the original content type.
|
||||
func ParseVendorSpecificContentType(cType string) string {
|
||||
plusIndex := strings.Index(cType, "+")
|
||||
|
||||
if plusIndex == -1 {
|
||||
return cType
|
||||
}
|
||||
|
||||
var parsableType string
|
||||
if semiColonIndex := strings.Index(cType, ";"); semiColonIndex == -1 {
|
||||
parsableType = cType[plusIndex+1:]
|
||||
} else if plusIndex < semiColonIndex {
|
||||
parsableType = cType[plusIndex+1 : semiColonIndex]
|
||||
} else {
|
||||
return cType[:semiColonIndex]
|
||||
}
|
||||
|
||||
slashIndex := strings.Index(cType, "/")
|
||||
|
||||
if slashIndex == -1 {
|
||||
return cType
|
||||
}
|
||||
|
||||
return cType[0:slashIndex+1] + parsableType
|
||||
}
|
||||
|
||||
// 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]
|
||||
}
|
||||
|
||||
// NOTE: Keep this in sync with the status code list
|
||||
var statusMessage = []string{
|
||||
100: "Continue", // StatusContinue
|
||||
101: "Switching Protocols", // StatusSwitchingProtocols
|
||||
102: "Processing", // StatusProcessing
|
||||
103: "Early Hints", // StatusEarlyHints
|
||||
|
||||
200: "OK", // StatusOK
|
||||
201: "Created", // StatusCreated
|
||||
202: "Accepted", // StatusAccepted
|
||||
203: "Non-Authoritative Information", // StatusNonAuthoritativeInformation
|
||||
204: "No Content", // StatusNoContent
|
||||
205: "Reset Content", // StatusResetContent
|
||||
206: "Partial Content", // StatusPartialContent
|
||||
207: "Multi-Status", // StatusMultiStatus
|
||||
208: "Already Reported", // StatusAlreadyReported
|
||||
226: "IM Used", // StatusIMUsed
|
||||
|
||||
300: "Multiple Choices", // StatusMultipleChoices
|
||||
301: "Moved Permanently", // StatusMovedPermanently
|
||||
302: "Found", // StatusFound
|
||||
303: "See Other", // StatusSeeOther
|
||||
304: "Not Modified", // StatusNotModified
|
||||
305: "Use Proxy", // StatusUseProxy
|
||||
306: "Switch Proxy", // StatusSwitchProxy
|
||||
307: "Temporary Redirect", // StatusTemporaryRedirect
|
||||
308: "Permanent Redirect", // StatusPermanentRedirect
|
||||
|
||||
400: "Bad Request", // StatusBadRequest
|
||||
401: "Unauthorized", // StatusUnauthorized
|
||||
402: "Payment Required", // StatusPaymentRequired
|
||||
403: "Forbidden", // StatusForbidden
|
||||
404: "Not Found", // StatusNotFound
|
||||
405: "Method Not Allowed", // StatusMethodNotAllowed
|
||||
406: "Not Acceptable", // StatusNotAcceptable
|
||||
407: "Proxy Authentication Required", // StatusProxyAuthRequired
|
||||
408: "Request Timeout", // StatusRequestTimeout
|
||||
409: "Conflict", // StatusConflict
|
||||
410: "Gone", // StatusGone
|
||||
411: "Length Required", // StatusLengthRequired
|
||||
412: "Precondition Failed", // StatusPreconditionFailed
|
||||
413: "Request Entity Too Large", // StatusRequestEntityTooLarge
|
||||
414: "Request URI Too Long", // StatusRequestURITooLong
|
||||
415: "Unsupported Media Type", // StatusUnsupportedMediaType
|
||||
416: "Requested Range Not Satisfiable", // StatusRequestedRangeNotSatisfiable
|
||||
417: "Expectation Failed", // StatusExpectationFailed
|
||||
418: "I'm a teapot", // StatusTeapot
|
||||
421: "Misdirected Request", // StatusMisdirectedRequest
|
||||
422: "Unprocessable Entity", // StatusUnprocessableEntity
|
||||
423: "Locked", // StatusLocked
|
||||
424: "Failed Dependency", // StatusFailedDependency
|
||||
425: "Too Early", // StatusTooEarly
|
||||
426: "Upgrade Required", // StatusUpgradeRequired
|
||||
428: "Precondition Required", // StatusPreconditionRequired
|
||||
429: "Too Many Requests", // StatusTooManyRequests
|
||||
431: "Request Header Fields Too Large", // StatusRequestHeaderFieldsTooLarge
|
||||
451: "Unavailable For Legal Reasons", // StatusUnavailableForLegalReasons
|
||||
|
||||
500: "Internal Server Error", // StatusInternalServerError
|
||||
501: "Not Implemented", // StatusNotImplemented
|
||||
502: "Bad Gateway", // StatusBadGateway
|
||||
503: "Service Unavailable", // StatusServiceUnavailable
|
||||
504: "Gateway Timeout", // StatusGatewayTimeout
|
||||
505: "HTTP Version Not Supported", // StatusHTTPVersionNotSupported
|
||||
506: "Variant Also Negotiates", // StatusVariantAlsoNegotiates
|
||||
507: "Insufficient Storage", // StatusInsufficientStorage
|
||||
508: "Loop Detected", // StatusLoopDetected
|
||||
510: "Not Extended", // StatusNotExtended
|
||||
511: "Network Authentication Required", // StatusNetworkAuthenticationRequired
|
||||
}
|
||||
|
||||
// MIME types were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates:
|
||||
// - Use "application/xml" instead of "text/xml" as recommended per https://datatracker.ietf.org/doc/html/rfc7303#section-4.1
|
||||
// - Use "text/javascript" instead of "application/javascript" as recommended per https://www.rfc-editor.org/rfc/rfc9239#name-text-javascript
|
||||
var mimeExtensions = map[string]string{
|
||||
"html": "text/html",
|
||||
"htm": "text/html",
|
||||
"shtml": "text/html",
|
||||
"css": "text/css",
|
||||
"xml": "application/xml",
|
||||
"gif": "image/gif",
|
||||
"jpeg": "image/jpeg",
|
||||
"jpg": "image/jpeg",
|
||||
"js": "text/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",
|
||||
"avif": "image/avif",
|
||||
"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",
|
||||
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"wmlc": "application/vnd.wap.wmlc",
|
||||
"wasm": "application/wasm",
|
||||
"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",
|
||||
}
|
148
utils/http_test.go
Normal file
148
utils/http_test.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
// ⚡️ 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_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)
|
||||
|
||||
err := mime.AddExtensionType(".mjs", "application/javascript")
|
||||
if err == nil {
|
||||
res = GetMIME(".mjs")
|
||||
AssertEqual(t, "application/javascript", res)
|
||||
}
|
||||
AssertEqual(t, nil, err)
|
||||
|
||||
// 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_ParseVendorSpecificContentType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cType := ParseVendorSpecificContentType("application/json")
|
||||
AssertEqual(t, "application/json", cType)
|
||||
|
||||
cType = ParseVendorSpecificContentType("multipart/form-data; boundary=dart-http-boundary-ZnVy.ICWq+7HOdsHqWxCFa8g3D.KAhy+Y0sYJ_lBADypu8po3_X")
|
||||
AssertEqual(t, "multipart/form-data", cType)
|
||||
|
||||
cType = ParseVendorSpecificContentType("multipart/form-data")
|
||||
AssertEqual(t, "multipart/form-data", cType)
|
||||
|
||||
cType = ParseVendorSpecificContentType("application/vnd.api+json; version=1")
|
||||
AssertEqual(t, "application/json", cType)
|
||||
|
||||
cType = ParseVendorSpecificContentType("application/vnd.api+json")
|
||||
AssertEqual(t, "application/json", cType)
|
||||
|
||||
cType = ParseVendorSpecificContentType("application/vnd.dummy+x-www-form-urlencoded")
|
||||
AssertEqual(t, "application/x-www-form-urlencoded", cType)
|
||||
|
||||
cType = ParseVendorSpecificContentType("something invalid")
|
||||
AssertEqual(t, "something invalid", cType)
|
||||
}
|
||||
|
||||
func Benchmark_ParseVendorSpecificContentType(b *testing.B) {
|
||||
var cType string
|
||||
b.Run("vendorContentType", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
cType = ParseVendorSpecificContentType("application/vnd.api+json; version=1")
|
||||
}
|
||||
AssertEqual(b, "application/json", cType)
|
||||
})
|
||||
|
||||
b.Run("defaultContentType", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
cType = ParseVendorSpecificContentType("application/json")
|
||||
}
|
||||
AssertEqual(b, "application/json", cType)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_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)
|
||||
})
|
||||
}
|
143
utils/ips.go
Normal file
143
utils/ips.go
Normal file
|
@ -0,0 +1,143 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// IsIPv4 works the same way as net.ParseIP,
|
||||
// but without check for IPv6 case and without returning net.IP slice, whereby IsIPv4 makes no allocations.
|
||||
func IsIPv4(s string) bool {
|
||||
for i := 0; i < net.IPv4len; i++ {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
if s[0] != '.' {
|
||||
return false
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
|
||||
n, ci := 0, 0
|
||||
|
||||
for ci = 0; ci < len(s) && '0' <= s[ci] && s[ci] <= '9'; ci++ {
|
||||
n = n*10 + int(s[ci]-'0')
|
||||
if n > 0xFF {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if ci == 0 || (ci > 1 && s[0] == '0') {
|
||||
return false
|
||||
}
|
||||
|
||||
s = s[ci:]
|
||||
}
|
||||
|
||||
return len(s) == 0
|
||||
}
|
||||
|
||||
// IsIPv6 works the same way as net.ParseIP,
|
||||
// but without check for IPv4 case and without returning net.IP slice, whereby IsIPv6 makes no allocations.
|
||||
func IsIPv6(s string) bool {
|
||||
ellipsis := -1 // position of ellipsis in ip
|
||||
|
||||
// Might have leading ellipsis
|
||||
if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
|
||||
ellipsis = 0
|
||||
s = s[2:]
|
||||
// Might be only ellipsis
|
||||
if len(s) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Loop, parsing hex numbers followed by colon.
|
||||
i := 0
|
||||
for i < net.IPv6len {
|
||||
// Hex number.
|
||||
n, ci := 0, 0
|
||||
|
||||
for ci = 0; ci < len(s); ci++ {
|
||||
if '0' <= s[ci] && s[ci] <= '9' {
|
||||
n *= 16
|
||||
n += int(s[ci] - '0')
|
||||
} else if 'a' <= s[ci] && s[ci] <= 'f' {
|
||||
n *= 16
|
||||
n += int(s[ci]-'a') + 10
|
||||
} else if 'A' <= s[ci] && s[ci] <= 'F' {
|
||||
n *= 16
|
||||
n += int(s[ci]-'A') + 10
|
||||
} else {
|
||||
break
|
||||
}
|
||||
if n > 0xFFFF {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if ci == 0 || n > 0xFFFF {
|
||||
return false
|
||||
}
|
||||
|
||||
if ci < len(s) && s[ci] == '.' {
|
||||
if ellipsis < 0 && i != net.IPv6len-net.IPv4len {
|
||||
return false
|
||||
}
|
||||
if i+net.IPv4len > net.IPv6len {
|
||||
return false
|
||||
}
|
||||
|
||||
if !IsIPv4(s) {
|
||||
return false
|
||||
}
|
||||
|
||||
s = ""
|
||||
i += net.IPv4len
|
||||
break
|
||||
}
|
||||
|
||||
// Save this 16-bit chunk.
|
||||
i += 2
|
||||
|
||||
// Stop at end of string.
|
||||
s = s[ci:]
|
||||
if len(s) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Otherwise must be followed by colon and more.
|
||||
if s[0] != ':' || len(s) == 1 {
|
||||
return false
|
||||
}
|
||||
s = s[1:]
|
||||
|
||||
// Look for ellipsis.
|
||||
if s[0] == ':' {
|
||||
if ellipsis >= 0 { // already have one
|
||||
return false
|
||||
}
|
||||
ellipsis = i
|
||||
s = s[1:]
|
||||
if len(s) == 0 { // can be at end
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must have used entire string.
|
||||
if len(s) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// If didn't parse enough, expand ellipsis.
|
||||
if i < net.IPv6len {
|
||||
if ellipsis < 0 {
|
||||
return false
|
||||
}
|
||||
} else if ellipsis >= 0 {
|
||||
// Ellipsis must represent at least one 0 group.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
91
utils/ips_test.go
Normal file
91
utils/ips_test.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
// ⚡️ 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 (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_IsIPv4(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
AssertEqual(t, true, IsIPv4("174.23.33.100"))
|
||||
AssertEqual(t, true, IsIPv4("127.0.0.1"))
|
||||
AssertEqual(t, true, IsIPv4("127.255.255.255"))
|
||||
AssertEqual(t, true, IsIPv4("0.0.0.0"))
|
||||
|
||||
AssertEqual(t, false, IsIPv4(".0.0.0"))
|
||||
AssertEqual(t, false, IsIPv4("0.0.0."))
|
||||
AssertEqual(t, false, IsIPv4("0.0.0"))
|
||||
AssertEqual(t, false, IsIPv4(".0.0.0."))
|
||||
AssertEqual(t, false, IsIPv4("0.0.0.0.0"))
|
||||
AssertEqual(t, false, IsIPv4("0"))
|
||||
AssertEqual(t, false, IsIPv4(""))
|
||||
AssertEqual(t, false, IsIPv4("2345:0425:2CA1::0567:5673:23b5"))
|
||||
AssertEqual(t, false, IsIPv4("invalid"))
|
||||
AssertEqual(t, false, IsIPv4("189.12.34.260"))
|
||||
AssertEqual(t, false, IsIPv4("189.12.260.260"))
|
||||
AssertEqual(t, false, IsIPv4("189.260.260.260"))
|
||||
AssertEqual(t, false, IsIPv4("999.999.999.999"))
|
||||
AssertEqual(t, false, IsIPv4("9999.9999.9999.9999"))
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=UnsafeString -benchmem -count=2
|
||||
|
||||
func Benchmark_IsIPv4(b *testing.B) {
|
||||
ip := "174.23.33.100"
|
||||
var res bool
|
||||
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = IsIPv4(ip)
|
||||
}
|
||||
AssertEqual(b, true, res)
|
||||
})
|
||||
|
||||
b.Run("default", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = net.ParseIP(ip) != nil
|
||||
}
|
||||
AssertEqual(b, true, res)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IsIPv6(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
AssertEqual(t, true, IsIPv6("9396:9549:b4f7:8ed0:4791:1330:8c06:e62d"))
|
||||
AssertEqual(t, true, IsIPv6("2345:0425:2CA1::0567:5673:23b5"))
|
||||
AssertEqual(t, true, IsIPv6("2001:1:2:3:4:5:6:7"))
|
||||
|
||||
AssertEqual(t, false, IsIPv6("1.1.1.1"))
|
||||
AssertEqual(t, false, IsIPv6("2001:1:2:3:4:5:6:"))
|
||||
AssertEqual(t, false, IsIPv6(":1:2:3:4:5:6:"))
|
||||
AssertEqual(t, false, IsIPv6("1:2:3:4:5:6:"))
|
||||
AssertEqual(t, false, IsIPv6(""))
|
||||
AssertEqual(t, false, IsIPv6("invalid"))
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=UnsafeString -benchmem -count=2
|
||||
|
||||
func Benchmark_IsIPv6(b *testing.B) {
|
||||
ip := "9396:9549:b4f7:8ed0:4791:1330:8c06:e62d"
|
||||
var res bool
|
||||
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = IsIPv6(ip)
|
||||
}
|
||||
AssertEqual(b, true, res)
|
||||
})
|
||||
|
||||
b.Run("default", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = net.ParseIP(ip) != nil
|
||||
}
|
||||
AssertEqual(b, true, res)
|
||||
})
|
||||
}
|
9
utils/json.go
Normal file
9
utils/json.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package utils
|
||||
|
||||
// JSONMarshal returns the JSON encoding of v.
|
||||
type JSONMarshal func(v interface{}) ([]byte, error)
|
||||
|
||||
// JSONUnmarshal parses the JSON-encoded data and stores the result
|
||||
// in the value pointed to by v. If v is nil or not a pointer,
|
||||
// Unmarshal returns an InvalidUnmarshalError.
|
||||
type JSONUnmarshal func(data []byte, v interface{}) error
|
58
utils/json_test.go
Normal file
58
utils/json_test.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type sampleStructure struct {
|
||||
ImportantString string `json:"important_string"`
|
||||
}
|
||||
|
||||
func Test_GolangJSONEncoder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ss = &sampleStructure{
|
||||
ImportantString: "Hello World",
|
||||
}
|
||||
importantString = `{"important_string":"Hello World"}`
|
||||
jsonEncoder JSONMarshal = json.Marshal
|
||||
)
|
||||
|
||||
raw, err := jsonEncoder(ss)
|
||||
AssertEqual(t, err, nil)
|
||||
|
||||
AssertEqual(t, string(raw), importantString)
|
||||
}
|
||||
|
||||
func Test_DefaultJSONEncoder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ss = &sampleStructure{
|
||||
ImportantString: "Hello World",
|
||||
}
|
||||
importantString = `{"important_string":"Hello World"}`
|
||||
jsonEncoder JSONMarshal = json.Marshal
|
||||
)
|
||||
|
||||
raw, err := jsonEncoder(ss)
|
||||
AssertEqual(t, err, nil)
|
||||
|
||||
AssertEqual(t, string(raw), importantString)
|
||||
}
|
||||
|
||||
func Test_DefaultJSONDecoder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ss sampleStructure
|
||||
importantString = []byte(`{"important_string":"Hello World"}`)
|
||||
jsonDecoder JSONUnmarshal = json.Unmarshal
|
||||
)
|
||||
|
||||
err := jsonDecoder(importantString, &ss)
|
||||
AssertEqual(t, err, nil)
|
||||
AssertEqual(t, "Hello World", ss.ImportantString)
|
||||
}
|
75
utils/strings.go
Normal file
75
utils/strings.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
// ⚡️ 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 converts ascii string to lower-case
|
||||
func ToLower(b string) string {
|
||||
res := make([]byte, len(b))
|
||||
copy(res, b)
|
||||
for i := 0; i < len(res); i++ {
|
||||
res[i] = toLowerTable[res[i]]
|
||||
}
|
||||
|
||||
return UnsafeString(res)
|
||||
}
|
||||
|
||||
// ToUpper converts ascii string to upper-case
|
||||
func ToUpper(b string) string {
|
||||
res := make([]byte, len(b))
|
||||
copy(res, b)
|
||||
for i := 0; i < len(res); i++ {
|
||||
res[i] = toUpperTable[res[i]]
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
|
||||
// EqualFold tests ascii strings for equality case-insensitively
|
||||
func EqualFold(b, s string) 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
|
||||
}
|
217
utils/strings_test.go
Normal file
217
utils/strings_test.go
Normal file
|
@ -0,0 +1,217 @@
|
|||
// ⚡️ 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_ToUpper(t *testing.T) {
|
||||
t.Parallel()
|
||||
res := ToUpper("/my/name/is/:param/*")
|
||||
AssertEqual(t, "/MY/NAME/IS/:PARAM/*", res)
|
||||
}
|
||||
|
||||
const (
|
||||
largeStr = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts/RePos/GoFiBer/FibEr/iSsues/CoMmEnts"
|
||||
upperStr = "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS/REPOS/GOFIBER/FIBER/ISSUES/COMMENTS"
|
||||
lowerStr = "/repos/gofiber/fiber/issues/187643/comments/repos/gofiber/fiber/issues/comments"
|
||||
)
|
||||
|
||||
func Benchmark_ToUpper(b *testing.B) {
|
||||
var res string
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = ToUpper(largeStr)
|
||||
}
|
||||
AssertEqual(b, upperStr, res)
|
||||
})
|
||||
b.Run("default", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = strings.ToUpper(largeStr)
|
||||
}
|
||||
AssertEqual(b, upperStr, res)
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func Benchmark_ToLower(b *testing.B) {
|
||||
var res string
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = ToLower(largeStr)
|
||||
}
|
||||
AssertEqual(b, lowerStr, res)
|
||||
})
|
||||
b.Run("default", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = strings.ToLower(largeStr)
|
||||
}
|
||||
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)
|
||||
}
|
32
utils/time.go
Normal file
32
utils/time.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
timestampTimer sync.Once
|
||||
// Timestamp please start the timer function before you use this value
|
||||
// please load the value with atomic `atomic.LoadUint32(&utils.Timestamp)`
|
||||
Timestamp uint32
|
||||
)
|
||||
|
||||
// StartTimeStampUpdater starts a concurrent function which stores the timestamp to an atomic value per second,
|
||||
// which is much better for performance than determining it at runtime each time
|
||||
func StartTimeStampUpdater() {
|
||||
timestampTimer.Do(func() {
|
||||
// set initial value
|
||||
atomic.StoreUint32(&Timestamp, uint32(time.Now().Unix()))
|
||||
go func(sleep time.Duration) {
|
||||
ticker := time.NewTicker(sleep)
|
||||
defer ticker.Stop()
|
||||
|
||||
for t := range ticker.C {
|
||||
// update timestamp
|
||||
atomic.StoreUint32(&Timestamp, uint32(t.Unix()))
|
||||
}
|
||||
}(1 * time.Second) // duration
|
||||
})
|
||||
}
|
48
utils/time_test.go
Normal file
48
utils/time_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func checkTimeStamp(tb testing.TB, expectedCurrent, actualCurrent uint32) { //nolint:thelper // TODO: Verify if tb can be nil
|
||||
if tb != nil {
|
||||
tb.Helper()
|
||||
}
|
||||
// test with some buffer in front and back of the expectedCurrent time -> because of the timing on the work machine
|
||||
AssertEqual(tb, true, actualCurrent >= expectedCurrent-1 || actualCurrent <= expectedCurrent+1)
|
||||
}
|
||||
|
||||
func Test_TimeStampUpdater(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
StartTimeStampUpdater()
|
||||
|
||||
now := uint32(time.Now().Unix())
|
||||
checkTimeStamp(t, now, atomic.LoadUint32(&Timestamp))
|
||||
// one second later
|
||||
time.Sleep(1 * time.Second)
|
||||
checkTimeStamp(t, now+1, atomic.LoadUint32(&Timestamp))
|
||||
// two seconds later
|
||||
time.Sleep(1 * time.Second)
|
||||
checkTimeStamp(t, now+2, atomic.LoadUint32(&Timestamp))
|
||||
}
|
||||
|
||||
func Benchmark_CalculateTimestamp(b *testing.B) {
|
||||
StartTimeStampUpdater()
|
||||
|
||||
var res uint32
|
||||
b.Run("fiber", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = atomic.LoadUint32(&Timestamp)
|
||||
}
|
||||
checkTimeStamp(b, uint32(time.Now().Unix()), res)
|
||||
})
|
||||
b.Run("default", func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
res = uint32(time.Now().Unix())
|
||||
}
|
||||
checkTimeStamp(b, uint32(time.Now().Unix()), res)
|
||||
})
|
||||
}
|
4
utils/xml.go
Normal file
4
utils/xml.go
Normal file
|
@ -0,0 +1,4 @@
|
|||
package utils
|
||||
|
||||
// XMLMarshal returns the XML encoding of v.
|
||||
type XMLMarshal func(v interface{}) ([]byte, error)
|
59
utils/xml_test.go
Normal file
59
utils/xml_test.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type serversXMLStructure struct {
|
||||
XMLName xml.Name `xml:"servers"`
|
||||
Version string `xml:"version,attr"`
|
||||
Servers []serverXMLStructure `xml:"server"`
|
||||
}
|
||||
|
||||
type serverXMLStructure struct {
|
||||
XMLName xml.Name `xml:"server"`
|
||||
Name string `xml:"name"`
|
||||
}
|
||||
|
||||
const xmlString = `<servers version="1"><server><name>fiber one</name></server><server><name>fiber two</name></server></servers>`
|
||||
|
||||
func Test_GolangXMLEncoder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ss = &serversXMLStructure{
|
||||
Version: "1",
|
||||
Servers: []serverXMLStructure{
|
||||
{Name: "fiber one"},
|
||||
{Name: "fiber two"},
|
||||
},
|
||||
}
|
||||
xmlEncoder XMLMarshal = xml.Marshal
|
||||
)
|
||||
|
||||
raw, err := xmlEncoder(ss)
|
||||
AssertEqual(t, err, nil)
|
||||
|
||||
AssertEqual(t, string(raw), xmlString)
|
||||
}
|
||||
|
||||
func Test_DefaultXMLEncoder(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ss = &serversXMLStructure{
|
||||
Version: "1",
|
||||
Servers: []serverXMLStructure{
|
||||
{Name: "fiber one"},
|
||||
{Name: "fiber two"},
|
||||
},
|
||||
}
|
||||
xmlEncoder XMLMarshal = xml.Marshal
|
||||
)
|
||||
|
||||
raw, err := xmlEncoder(ss)
|
||||
AssertEqual(t, err, nil)
|
||||
|
||||
AssertEqual(t, string(raw), xmlString)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue