-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(utils): add common utils package
- Loading branch information
Showing
23 changed files
with
1,319 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
go 1.21 | ||
|
||
use ( | ||
./ | ||
./errors | ||
./utils | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[![GoDoc](https://godoc.org/github.com/Cleverse/go-utilities/utils?status.svg)](http://godoc.org/github.com/Cleverse/go-utilities/utils) | ||
[![Report card](https://goreportcard.com/badge/github.com/Cleverse/go-utilities/utils)](https://goreportcard.com/report/github.com/Cleverse/go-utilities/utils) | ||
|
||
# utils | ||
|
||
Optimized common generic utilities for Cleverse projects. | ||
|
||
## Installation | ||
|
||
```shell | ||
go get github.com/Cleverse/go-utilities/utils | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package utils | ||
|
||
import ( | ||
"crypto/rand" | ||
) | ||
|
||
// RandomBytes returns a random byte slice with the given length with crypto/rand. | ||
func RandomBytes(length int) []byte { | ||
b := make([]byte, length) | ||
Must(rand.Read(b)) | ||
return b | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package utils | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/Cleverse/go-utilities/errors" | ||
) | ||
|
||
// Must is used to simplify error handling. | ||
// It's helpful to wraps a call to a function returning a value and an error. and panics if err is error or false. | ||
// | ||
// warning: this is not safe, use with caution!! (avoid to use it's in runtime) | ||
func Must[T any](data T, err any, messageArgs ...interface{}) T { | ||
must(err, messageArgs...) | ||
return data | ||
} | ||
|
||
// MustNotError is used to simplify error handling. | ||
// | ||
// warning: this is not safe, use with caution!! (avoid to use it's in runtime) | ||
func MustNotError[T any](data T, err error) T { | ||
if err != nil { | ||
panic(errors.WithStack(err)) | ||
} | ||
return data | ||
} | ||
|
||
// UnsafeMust is used to simplify error/ok handling by ignoring it in runtime. | ||
// | ||
// warning: this is not safe, use with caution!! | ||
// be careful when value is pointer, it may be nil. (safe in runtime, but need to check nil before use) | ||
func UnsafeMust[T any, E any](data T, e E) T { | ||
return data | ||
} | ||
|
||
// MustOK is used to simplify ok handling. | ||
// for case ok should be true. | ||
// | ||
// warning: this is not safe, use with caution!! (avoid to use it's in runtime) | ||
func MustOK[T any](data T, ok bool) T { | ||
if !ok { | ||
panic(errors.Errorf("got not ok, but should ok")) | ||
} | ||
return data | ||
} | ||
|
||
// MustNotOK is used to simplify ok handling. | ||
// for case ok should be false. | ||
// | ||
// warning: this is not safe, use with caution!! (avoid to use it's in runtime) | ||
func MustNotOK[T any](data T, ok bool) T { | ||
if ok { | ||
panic(errors.Errorf("got ok, but should not ok")) | ||
} | ||
return data | ||
} | ||
|
||
// must panics if err is error or false. | ||
func must(err any, messageArgs ...interface{}) { | ||
if err == nil { | ||
return | ||
} | ||
|
||
switch e := err.(type) { | ||
case bool: | ||
if !e { | ||
panic(Default[string](msgFormatter(messageArgs...), "not ok")) | ||
} | ||
case error: | ||
if e == nil { | ||
return | ||
} | ||
message := msgFormatter(messageArgs...) | ||
if message != "" { | ||
panic(message + ": " + e.Error()) | ||
} | ||
panic(errors.WithStack(e)) | ||
default: | ||
panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error") | ||
} | ||
} | ||
|
||
func msgFormatter(msgAndArgs ...interface{}) string { | ||
switch len(msgAndArgs) { | ||
case 0: | ||
return "" | ||
case 1: | ||
if msgAsStr, ok := msgAndArgs[0].(string); ok { | ||
return msgAsStr | ||
} | ||
return fmt.Sprintf("%+v", msgAndArgs[0]) | ||
default: | ||
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module github.com/Cleverse/go-utilities/utils | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/Cleverse/go-utilities/errors v0.0.0-20231019072721-442842e3dc09 | ||
github.com/stretchr/testify v1.8.4 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
github.com/Cleverse/go-utilities/errors v0.0.0-20231019072721-442842e3dc09 h1:kNAgub14cUBr7uoTxQSrf0qiMNJSzMhIDa8RFdv6hkk= | ||
github.com/Cleverse/go-utilities/errors v0.0.0-20231019072721-442842e3dc09/go.mod h1:1QK+h746G1DwellQ6KK2rBCJusZqIDTZ9QFVGnUX9+Q= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package utils | ||
|
||
import ( | ||
"encoding/hex" | ||
|
||
"github.com/Cleverse/go-utilities/errors" | ||
) | ||
|
||
// RandomHex returns a random hex string with the given length. | ||
func RandomHex(length int) string { | ||
// TODO: reduce memory allocation by using a same buffer for random and hex encoding. | ||
return hex.EncodeToString(RandomBytes(length)) | ||
} | ||
|
||
// Has0xPrefix checks if the input string has 0x prefix or not. | ||
// | ||
// Returns `true“ if the input string has 0x prefix, otherwise `false`. | ||
func Has0xPrefix(input string) bool { | ||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') | ||
} | ||
|
||
// Trim0xPrefix returns the input string without 0x prefix. | ||
func Trim0xPrefix(input string) string { | ||
if Has0xPrefix(input) { | ||
return input[2:] | ||
} | ||
return input | ||
} | ||
|
||
// Add0xPrefix returns the input string with 0x prefix. | ||
func Add0xPrefix(input string) string { | ||
if !Has0xPrefix(input) { | ||
return "0x" + input | ||
} | ||
return input | ||
} | ||
|
||
// Flip0xPrefix returns the input string with 0x prefix if it doesn't have 0x prefix, otherwise returns the input string without 0x prefix. | ||
func Flip0xPrefix(input string) string { | ||
if Has0xPrefix(input) { | ||
return input[2:] | ||
} | ||
return "0x" + input | ||
} | ||
|
||
// IsHex verifies whether a string can represent a valid hex-encoded or not. | ||
func IsHex(str string) bool { | ||
str = Trim0xPrefix(str) | ||
for _, c := range []byte(str) { | ||
if !isHexCharacter(c) { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// isHexCharacter returns bool of c being a valid hexadecimal. | ||
func isHexCharacter(c byte) bool { | ||
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F') | ||
} | ||
|
||
// DecodeHex decodes a hex string into a byte slice. str can be prefixed with 0x. | ||
func DecodeHex(str string) ([]byte, error) { | ||
b, err := hex.DecodeString(Trim0xPrefix(str)) | ||
return b, errors.WithStack(err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package utils | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestHex0XPrefix(t *testing.T) { | ||
type TestCase struct { | ||
Input string | ||
Has0xPrefix bool | ||
Func func(string) string | ||
Expected string | ||
} | ||
|
||
testCases := []TestCase{ | ||
{ | ||
Input: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: true, | ||
Func: Trim0xPrefix, | ||
Expected: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
{ | ||
Input: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: true, | ||
Func: Add0xPrefix, | ||
Expected: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
{ | ||
Input: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: true, | ||
Func: Flip0xPrefix, | ||
Expected: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
{ | ||
Input: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: false, | ||
Func: Trim0xPrefix, | ||
Expected: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
{ | ||
Input: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: false, | ||
Func: Add0xPrefix, | ||
Expected: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
{ | ||
Input: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: false, | ||
Func: Flip0xPrefix, | ||
Expected: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
|
||
{ | ||
Input: "0XEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: true, | ||
Func: Trim0xPrefix, | ||
Expected: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
{ | ||
Input: "0XEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: true, | ||
Func: Add0xPrefix, | ||
Expected: "0XEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
{ | ||
Input: "0XEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
Has0xPrefix: true, | ||
Func: Flip0xPrefix, | ||
Expected: "EeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.Input, func(t *testing.T) { | ||
assert := assert.New(t) | ||
assert.Equal(tc.Has0xPrefix, Has0xPrefix(tc.Input), "Has0xPrefix should be equal") | ||
assert.Equal(tc.Expected, tc.Func(tc.Input), "actual result from `Func(string) string` should equal to expected") | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package utils | ||
|
||
// CopyMapOfArray high performance copy map of array | ||
// to new map of array with shared backing array. | ||
// Reference: https://cs.opensource.google/go/go/+/master:src/net/http/header.go;l=94 | ||
func CopyMapOfArray[K comparable, V any](src map[K][]V) map[K][]V { | ||
if src == nil { | ||
return nil | ||
} | ||
|
||
// Find total number of values. | ||
totalValue := 0 | ||
for _, val := range src { | ||
totalValue += len(val) | ||
} | ||
|
||
tmp := make([]V, totalValue) // use shared backing array for reduce memory allocation. | ||
dst := make(map[K][]V, len(src)) | ||
for k, val := range src { | ||
if val == nil { | ||
dst[k] = nil | ||
continue | ||
} | ||
n := copy(tmp, val) // copy values to shared array. | ||
dst[k] = tmp[:n:n] // point to specific length and capacity of shared backing array. | ||
tmp = tmp[n:] // move pointer to next position. | ||
} | ||
|
||
return dst | ||
} |
Oops, something went wrong.