Skip to content

Commit 2052c30

Browse files
authored
Merge pull request #1 from KyberNetwork/phu/init
add some utils
2 parents 7b788a8 + 4c01fa6 commit 2052c30

File tree

13 files changed

+825
-0
lines changed

13 files changed

+825
-0
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
### Why
2+
3+
Reusable util code
4+
5+
### What
6+
7+
- ctx.go: Context that ignores being cancelled
8+
- map.go: Collects values from a slice
9+
- num.go: Conversions, Min, Max, Abs
10+
- slice.go: Checks for existence, maps with fn, gets unique elements, filters

ctx.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package kutils
2+
3+
import (
4+
"context"
5+
"time"
6+
)
7+
8+
type ctxWithoutCancel struct {
9+
ctx context.Context
10+
}
11+
12+
func (c ctxWithoutCancel) Deadline() (time.Time, bool) { return time.Time{}, false }
13+
func (c ctxWithoutCancel) Done() <-chan struct{} { return nil }
14+
func (c ctxWithoutCancel) Err() error { return nil }
15+
func (c ctxWithoutCancel) Value(key interface{}) interface{} { return c.ctx.Value(key) }
16+
17+
func CtxWithoutCancel(ctx context.Context) context.Context {
18+
if ctx == nil {
19+
return context.Background()
20+
}
21+
return &ctxWithoutCancel{ctx: ctx}
22+
}

ctx_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package kutils
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestCtxWithoutCancel(t *testing.T) {
12+
cancelledCtx, cancel := context.WithTimeout(
13+
context.WithValue(CtxWithoutCancel(nil), "key", "value"), time.Second)
14+
detachedCtx := CtxWithoutCancel(cancelledCtx)
15+
16+
t.Run("Deadline()", func(t *testing.T) {
17+
_, ok := cancelledCtx.Deadline()
18+
assert.True(t, ok, "cancelledCtx should have Deadline")
19+
_, ok = detachedCtx.Deadline()
20+
assert.False(t, ok, "detachedCtx should not have Deadline")
21+
cancel()
22+
})
23+
24+
t.Run("Err()", func(t *testing.T) {
25+
assert.NotNil(t, cancelledCtx.Err())
26+
assert.Nil(t, detachedCtx.Err())
27+
})
28+
29+
t.Run("Done()", func(t *testing.T) {
30+
select {
31+
case <-cancelledCtx.Done():
32+
default:
33+
assert.Fail(t, "cancelledCtx.Done() should be closed")
34+
}
35+
select {
36+
case <-detachedCtx.Done():
37+
assert.Fail(t, "detachedCtx.Done() should not be closed")
38+
default:
39+
}
40+
})
41+
42+
t.Run("Value()", func(t *testing.T) {
43+
assert.Equal(t, "value", cancelledCtx.Value("key"))
44+
assert.Equal(t, "value", detachedCtx.Value("key"))
45+
})
46+
}

go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module github.com/KyberNetwork/kutils
2+
3+
go 1.20
4+
5+
require (
6+
github.com/stretchr/testify v1.8.4
7+
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/pmezard/go-difflib v1.0.0 // indirect
13+
gopkg.in/yaml.v3 v3.0.1 // indirect
14+
)

go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
6+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
7+
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31 h1:9k5exFQKQglLo+RoP+4zMjOFE14P6+vyR0baDAi0Rcs=
8+
golang.org/x/exp v0.0.0-20231005195138-3e424a577f31/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
9+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
10+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
11+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
12+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

map.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package kutils
2+
3+
func Map[T any, K comparable, V any](lst []T, keyFn func(T) K, valFn func(T) V) map[K]V {
4+
res := make(map[K]V, len(lst))
5+
for _, elem := range lst {
6+
res[keyFn(elem)] = valFn(elem)
7+
}
8+
return res
9+
}
10+
11+
func MapMulti[T any, K comparable, V any](lst []T, keyFn func(T) K, valFn func(T) V) map[K][]V {
12+
res := make(map[K][]V)
13+
for _, elem := range lst {
14+
res[keyFn(elem)] = append(res[keyFn(elem)], valFn(elem))
15+
}
16+
return res
17+
}
18+
19+
func MapKey[T any, K comparable](lst []T, keyFn func(T) K) map[K]T {
20+
res := make(map[K]T, len(lst))
21+
for _, elem := range lst {
22+
res[keyFn(elem)] = elem
23+
}
24+
return res
25+
}
26+
27+
func MapKeyMulti[T any, K comparable](lst []T, keyFn func(T) K) map[K][]T {
28+
res := make(map[K][]T)
29+
for _, elem := range lst {
30+
res[keyFn(elem)] = append(res[keyFn(elem)], elem)
31+
}
32+
return res
33+
}

map_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package kutils
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestMap(t *testing.T) {
9+
testMap[int, int, string](t, "int to map[abs]string",
10+
[]int{-1, 2, -3, 2}, Abs[int], Itoa[int],
11+
map[int]string{1: "-1", 2: "2", 3: "-3"})
12+
testMap[uint, string, uint](t, "uint to map[string]cappedIncr",
13+
[]uint{1, 2, 9, 3}, Utoa[uint], func(u uint) uint { return Min(9, u+1) },
14+
map[string]uint{"1": 2, "2": 3, "9": 9, "3": 4})
15+
}
16+
17+
func testMap[T any, K comparable, V any](t *testing.T, name string, lst []T, keyFn func(T) K, valFn func(T) V,
18+
want map[K]V) {
19+
t.Run(name, func(t *testing.T) {
20+
if got := Map(lst, keyFn, valFn); !reflect.DeepEqual(got, want) {
21+
t.Errorf("Map() = %v, want %v", got, want)
22+
}
23+
})
24+
}
25+
26+
func TestMapMulti(t *testing.T) {
27+
testMapMulti[int, int, string](t, "int to map[abs][]string",
28+
[]int{-1, 2, -3, -2}, Abs[int], Itoa[int],
29+
map[int][]string{1: {"-1"}, 2: {"2", "-2"}, 3: {"-3"}})
30+
testMapMulti[uint, string, uint](t, "uint to map[string][]cappedIncr",
31+
[]uint{1, 2, 9, 3}, Utoa[uint], func(u uint) uint { return Min(9, u+1) },
32+
map[string][]uint{"1": {2}, "2": {3}, "9": {9}, "3": {4}})
33+
}
34+
35+
func testMapMulti[T any, K comparable, V any](t *testing.T, name string, lst []T, keyFn func(T) K, valFn func(T) V,
36+
want map[K][]V) {
37+
t.Run(name, func(t *testing.T) {
38+
if got := MapMulti(lst, keyFn, valFn); !reflect.DeepEqual(got, want) {
39+
t.Errorf("MapMulti() = %v, want %v", got, want)
40+
}
41+
})
42+
}
43+
44+
func TestMapKey(t *testing.T) {
45+
testMapKey[int, int](t, "int to map[abs]",
46+
[]int{-1, 2, -3, 2}, Abs[int],
47+
map[int]int{1: -1, 2: 2, 3: -3})
48+
testMapKey[uint, string](t, "uint to map[string]",
49+
[]uint{1, 2, 9, 3}, Utoa[uint],
50+
map[string]uint{"1": 1, "2": 2, "9": 9, "3": 3})
51+
}
52+
53+
func testMapKey[T any, K comparable](t *testing.T, name string, lst []T, keyFn func(T) K, want map[K]T) {
54+
t.Run(name, func(t *testing.T) {
55+
if got := MapKey(lst, keyFn); !reflect.DeepEqual(got, want) {
56+
t.Errorf("MapKey() = %v, want %v", got, want)
57+
}
58+
})
59+
}
60+
61+
func TestMapKeyMulti(t *testing.T) {
62+
testMapKeyMulti[int, int](t, "int to map[abs]",
63+
[]int{-1, 2, -3, -2}, Abs[int],
64+
map[int][]int{1: {-1}, 2: {2, -2}, 3: {-3}})
65+
testMapKeyMulti[uint, string](t, "uint to map[string]",
66+
[]uint{1, 2, 9, 3}, Utoa[uint],
67+
map[string][]uint{"1": {1}, "2": {2}, "9": {9}, "3": {3}})
68+
}
69+
70+
func testMapKeyMulti[T any, K comparable](t *testing.T, name string, lst []T, keyFn func(T) K, want map[K][]T) {
71+
t.Run(name, func(t *testing.T) {
72+
if got := MapKeyMulti(lst, keyFn); !reflect.DeepEqual(got, want) {
73+
t.Errorf("MapKeyMulti() = %v, want %v", got, want)
74+
}
75+
})
76+
}

num.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package kutils
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
7+
"golang.org/x/exp/constraints"
8+
)
9+
10+
func Itoa[T constraints.Signed](i T) string {
11+
return strconv.FormatInt(int64(i), 10)
12+
}
13+
14+
func Utoa[T constraints.Unsigned](u T) string {
15+
return strconv.FormatUint(uint64(u), 10)
16+
}
17+
18+
func Atoi[T constraints.Signed](a string) (i T, err error) {
19+
bitSize := 0
20+
switch any(i).(type) {
21+
case int:
22+
bitSize = strconv.IntSize
23+
case int8:
24+
bitSize = 8
25+
case int16:
26+
bitSize = 16
27+
case int32:
28+
bitSize = 32
29+
case int64:
30+
bitSize = 64
31+
}
32+
if i, err := strconv.ParseInt(a, 10, bitSize); err != nil {
33+
return 0, err
34+
} else if bitSize == 0 && int64(T(i)) != i {
35+
return 0, &strconv.NumError{Func: "ParseInt", Num: a, Err: strconv.ErrRange}
36+
} else {
37+
return T(i), nil
38+
}
39+
}
40+
41+
func Atou[T constraints.Unsigned](a string) (u T, err error) {
42+
bitSize := 0
43+
switch any(u).(type) {
44+
case uint, uintptr:
45+
bitSize = strconv.IntSize
46+
case uint8:
47+
bitSize = 8
48+
case uint16:
49+
bitSize = 16
50+
case uint32:
51+
bitSize = 32
52+
case uint64:
53+
bitSize = 64
54+
}
55+
if u, err := strconv.ParseUint(a, 10, bitSize); err != nil {
56+
return 0, err
57+
} else if bitSize == 0 && uint64(T(u)) != u {
58+
return 0, &strconv.NumError{Func: "ParseUint", Num: a, Err: strconv.ErrRange}
59+
} else {
60+
return T(u), nil
61+
}
62+
}
63+
64+
func Min[T constraints.Ordered](a, b T) T {
65+
if b < a {
66+
return b
67+
}
68+
return a
69+
}
70+
71+
func Max[T constraints.Ordered](a, b T) T {
72+
if b > a {
73+
return b
74+
}
75+
return a
76+
}
77+
78+
func Abs[T constraints.Signed | constraints.Float](a T) T {
79+
if a < 0 {
80+
if a-1 > 0 {
81+
panic(fmt.Sprintf("Abs result of %v will overflow", a))
82+
}
83+
return -a
84+
}
85+
return a
86+
}

0 commit comments

Comments
 (0)