1
0
Fork 0

Adding upstream version 2.52.6.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-17 06:50:16 +02:00
parent a960158181
commit 6d002e9543
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
441 changed files with 95392 additions and 0 deletions

90
utils/README.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}