Skip to content

Commit

Permalink
Merge pull request #1 from KyberNetwork/phu/init
Browse files Browse the repository at this point in the history
add some utils
  • Loading branch information
NgoKimPhu authored Nov 8, 2023
2 parents 7b788a8 + 4c01fa6 commit 2052c30
Show file tree
Hide file tree
Showing 13 changed files with 825 additions and 0 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
### Why

Reusable util code

### What

- ctx.go: Context that ignores being cancelled
- map.go: Collects values from a slice
- num.go: Conversions, Min, Max, Abs
- slice.go: Checks for existence, maps with fn, gets unique elements, filters
22 changes: 22 additions & 0 deletions ctx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package kutils

import (
"context"
"time"
)

type ctxWithoutCancel struct {
ctx context.Context
}

func (c ctxWithoutCancel) Deadline() (time.Time, bool) { return time.Time{}, false }
func (c ctxWithoutCancel) Done() <-chan struct{} { return nil }
func (c ctxWithoutCancel) Err() error { return nil }
func (c ctxWithoutCancel) Value(key interface{}) interface{} { return c.ctx.Value(key) }

func CtxWithoutCancel(ctx context.Context) context.Context {
if ctx == nil {
return context.Background()
}
return &ctxWithoutCancel{ctx: ctx}
}
46 changes: 46 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package kutils

import (
"context"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestCtxWithoutCancel(t *testing.T) {
cancelledCtx, cancel := context.WithTimeout(
context.WithValue(CtxWithoutCancel(nil), "key", "value"), time.Second)
detachedCtx := CtxWithoutCancel(cancelledCtx)

t.Run("Deadline()", func(t *testing.T) {
_, ok := cancelledCtx.Deadline()
assert.True(t, ok, "cancelledCtx should have Deadline")
_, ok = detachedCtx.Deadline()
assert.False(t, ok, "detachedCtx should not have Deadline")
cancel()
})

t.Run("Err()", func(t *testing.T) {
assert.NotNil(t, cancelledCtx.Err())
assert.Nil(t, detachedCtx.Err())
})

t.Run("Done()", func(t *testing.T) {
select {
case <-cancelledCtx.Done():
default:
assert.Fail(t, "cancelledCtx.Done() should be closed")
}
select {
case <-detachedCtx.Done():
assert.Fail(t, "detachedCtx.Done() should not be closed")
default:
}
})

t.Run("Value()", func(t *testing.T) {
assert.Equal(t, "value", cancelledCtx.Value("key"))
assert.Equal(t, "value", detachedCtx.Value("key"))
})
}
14 changes: 14 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module github.com/KyberNetwork/kutils

go 1.20

require (
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31
)

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
)
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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=
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 h1:9k5exFQKQglLo+RoP+4zMjOFE14P6+vyR0baDAi0Rcs=
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
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=
33 changes: 33 additions & 0 deletions map.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package kutils

func Map[T any, K comparable, V any](lst []T, keyFn func(T) K, valFn func(T) V) map[K]V {
res := make(map[K]V, len(lst))
for _, elem := range lst {
res[keyFn(elem)] = valFn(elem)
}
return res
}

func MapMulti[T any, K comparable, V any](lst []T, keyFn func(T) K, valFn func(T) V) map[K][]V {
res := make(map[K][]V)
for _, elem := range lst {
res[keyFn(elem)] = append(res[keyFn(elem)], valFn(elem))
}
return res
}

func MapKey[T any, K comparable](lst []T, keyFn func(T) K) map[K]T {
res := make(map[K]T, len(lst))
for _, elem := range lst {
res[keyFn(elem)] = elem
}
return res
}

func MapKeyMulti[T any, K comparable](lst []T, keyFn func(T) K) map[K][]T {
res := make(map[K][]T)
for _, elem := range lst {
res[keyFn(elem)] = append(res[keyFn(elem)], elem)
}
return res
}
76 changes: 76 additions & 0 deletions map_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package kutils

import (
"reflect"
"testing"
)

func TestMap(t *testing.T) {
testMap[int, int, string](t, "int to map[abs]string",
[]int{-1, 2, -3, 2}, Abs[int], Itoa[int],
map[int]string{1: "-1", 2: "2", 3: "-3"})
testMap[uint, string, uint](t, "uint to map[string]cappedIncr",
[]uint{1, 2, 9, 3}, Utoa[uint], func(u uint) uint { return Min(9, u+1) },
map[string]uint{"1": 2, "2": 3, "9": 9, "3": 4})
}

func testMap[T any, K comparable, V any](t *testing.T, name string, lst []T, keyFn func(T) K, valFn func(T) V,
want map[K]V) {
t.Run(name, func(t *testing.T) {
if got := Map(lst, keyFn, valFn); !reflect.DeepEqual(got, want) {
t.Errorf("Map() = %v, want %v", got, want)
}
})
}

func TestMapMulti(t *testing.T) {
testMapMulti[int, int, string](t, "int to map[abs][]string",
[]int{-1, 2, -3, -2}, Abs[int], Itoa[int],
map[int][]string{1: {"-1"}, 2: {"2", "-2"}, 3: {"-3"}})
testMapMulti[uint, string, uint](t, "uint to map[string][]cappedIncr",
[]uint{1, 2, 9, 3}, Utoa[uint], func(u uint) uint { return Min(9, u+1) },
map[string][]uint{"1": {2}, "2": {3}, "9": {9}, "3": {4}})
}

func testMapMulti[T any, K comparable, V any](t *testing.T, name string, lst []T, keyFn func(T) K, valFn func(T) V,
want map[K][]V) {
t.Run(name, func(t *testing.T) {
if got := MapMulti(lst, keyFn, valFn); !reflect.DeepEqual(got, want) {
t.Errorf("MapMulti() = %v, want %v", got, want)
}
})
}

func TestMapKey(t *testing.T) {
testMapKey[int, int](t, "int to map[abs]",
[]int{-1, 2, -3, 2}, Abs[int],
map[int]int{1: -1, 2: 2, 3: -3})
testMapKey[uint, string](t, "uint to map[string]",
[]uint{1, 2, 9, 3}, Utoa[uint],
map[string]uint{"1": 1, "2": 2, "9": 9, "3": 3})
}

func testMapKey[T any, K comparable](t *testing.T, name string, lst []T, keyFn func(T) K, want map[K]T) {
t.Run(name, func(t *testing.T) {
if got := MapKey(lst, keyFn); !reflect.DeepEqual(got, want) {
t.Errorf("MapKey() = %v, want %v", got, want)
}
})
}

func TestMapKeyMulti(t *testing.T) {
testMapKeyMulti[int, int](t, "int to map[abs]",
[]int{-1, 2, -3, -2}, Abs[int],
map[int][]int{1: {-1}, 2: {2, -2}, 3: {-3}})
testMapKeyMulti[uint, string](t, "uint to map[string]",
[]uint{1, 2, 9, 3}, Utoa[uint],
map[string][]uint{"1": {1}, "2": {2}, "9": {9}, "3": {3}})
}

func testMapKeyMulti[T any, K comparable](t *testing.T, name string, lst []T, keyFn func(T) K, want map[K][]T) {
t.Run(name, func(t *testing.T) {
if got := MapKeyMulti(lst, keyFn); !reflect.DeepEqual(got, want) {
t.Errorf("MapKeyMulti() = %v, want %v", got, want)
}
})
}
86 changes: 86 additions & 0 deletions num.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package kutils

import (
"fmt"
"strconv"

"golang.org/x/exp/constraints"
)

func Itoa[T constraints.Signed](i T) string {
return strconv.FormatInt(int64(i), 10)
}

func Utoa[T constraints.Unsigned](u T) string {
return strconv.FormatUint(uint64(u), 10)
}

func Atoi[T constraints.Signed](a string) (i T, err error) {
bitSize := 0
switch any(i).(type) {
case int:
bitSize = strconv.IntSize
case int8:
bitSize = 8
case int16:
bitSize = 16
case int32:
bitSize = 32
case int64:
bitSize = 64
}
if i, err := strconv.ParseInt(a, 10, bitSize); err != nil {
return 0, err
} else if bitSize == 0 && int64(T(i)) != i {
return 0, &strconv.NumError{Func: "ParseInt", Num: a, Err: strconv.ErrRange}
} else {
return T(i), nil
}
}

func Atou[T constraints.Unsigned](a string) (u T, err error) {
bitSize := 0
switch any(u).(type) {
case uint, uintptr:
bitSize = strconv.IntSize
case uint8:
bitSize = 8
case uint16:
bitSize = 16
case uint32:
bitSize = 32
case uint64:
bitSize = 64
}
if u, err := strconv.ParseUint(a, 10, bitSize); err != nil {
return 0, err
} else if bitSize == 0 && uint64(T(u)) != u {
return 0, &strconv.NumError{Func: "ParseUint", Num: a, Err: strconv.ErrRange}
} else {
return T(u), nil
}
}

func Min[T constraints.Ordered](a, b T) T {
if b < a {
return b
}
return a
}

func Max[T constraints.Ordered](a, b T) T {
if b > a {
return b
}
return a
}

func Abs[T constraints.Signed | constraints.Float](a T) T {
if a < 0 {
if a-1 > 0 {
panic(fmt.Sprintf("Abs result of %v will overflow", a))
}
return -a
}
return a
}
Loading

0 comments on commit 2052c30

Please sign in to comment.