diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e19ff6e..6986aa2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,11 +34,11 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: "1.19.x" + go-version: "1.21.x" - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.46.2 + version: v1.54.2 args: --config=.golangci.yml skip-pkg-cache: true skip-build-cache: true @@ -67,6 +67,6 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: "1.19.x" + go-version: "1.21.x" - name: Run test - run: go test -race -v ./... \ No newline at end of file + run: go test -race -v ./... diff --git a/.golangci.yml b/.golangci.yml index d51398f..d7eec16 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -21,6 +21,9 @@ linters: - goerr113 - ireturn - nonamedreturns + - depguard + - noctx + - structcheck # it shows elems in stack pkg is unused, but it is used :D linters-settings: funlen: # Checks the number of lines in a function. diff --git a/go.mod b/go.mod index 8153caf..ffd2fcb 100644 --- a/go.mod +++ b/go.mod @@ -1,18 +1,20 @@ module github.com/KyberNetwork/tradinglib -go 1.19 +go 1.21 require ( - github.com/ethereum/go-ethereum v1.10.26 + github.com/ethereum/go-ethereum v1.13.1 github.com/shopspring/decimal v1.3.1 + github.com/sourcegraph/conc v0.3.0 github.com/stretchr/testify v1.8.1 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/crypto v0.13.0 // indirect + golang.org/x/sys v0.12.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b3de538..20fa6ff 100644 --- a/go.sum +++ b/go.sum @@ -1,27 +1,39 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/ethereum/go-ethereum v1.10.26 h1:i/7d9RBBwiXCEuyduBQzJw/mKmnvzsN14jqBmytw72s= -github.com/ethereum/go-ethereum v1.10.26/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg= +github.com/ethereum/go-ethereum v1.13.1 h1:UF2FaUKPIy5jeZk3X06ait3y2Q4wI+vJ1l7+UARp+60= +github.com/ethereum/go-ethereum v1.13.1/go.mod h1:xHQKzwkHSl0gnSjZK1mWa06XEdm9685AHqhRknOzqGQ= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 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/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/ds/queue/queue.go b/pkg/ds/queue/queue.go new file mode 100644 index 0000000..d21f980 --- /dev/null +++ b/pkg/ds/queue/queue.go @@ -0,0 +1,155 @@ +package queue + +type Node[T any] struct { + value T + next *Node[T] + prev *Node[T] +} + +type Queue[T any] struct { + head *Node[T] + tail *Node[T] + size uint +} + +func New[T any]() *Queue[T] { + return &Queue[T]{ + head: nil, + tail: nil, + size: 0, + } +} + +func (q *Queue[T]) PushBack(val T) { + node := &Node[T]{ + value: val, + next: nil, + prev: nil, + } + + if q.IsEmpty() { + q.size++ + q.head = node + q.tail = node + return + } + + q.size++ + node.prev = q.tail + q.tail.next = node + q.tail = node +} + +func (q *Queue[T]) PushFront(val T) { + node := &Node[T]{ + value: val, + next: nil, + prev: nil, + } + + if q.IsEmpty() { + q.size++ + q.head = node + q.tail = node + return + } + + q.size++ + node.next = q.head + q.head.prev = node + q.head = node +} + +func (q *Queue[T]) PopBack() (T, bool) { + var t T + + if q.IsEmpty() { + return t, false + } + + t = q.tail.value + if q.size == 1 { + q.size-- + q.tail = nil + q.head = nil + return t, true + } + + q.size-- + if q.tail.prev != nil { + q.tail.prev.next = nil + } + q.tail = q.tail.prev + + return t, true +} + +func (q *Queue[T]) PopFront() (T, bool) { + var t T + + if q.IsEmpty() { + return t, false + } + + t = q.head.value + if q.size == 1 { + q.size-- + q.tail = nil + q.head = nil + return t, true + } + + q.size-- + if q.head.next != nil { + q.head.next.prev = nil + } + q.head = q.head.next + + return t, true +} + +func (q *Queue[T]) PeekBack() (T, bool) { + var t T + + if q.IsEmpty() { + return t, false + } + + t = q.tail.value + return t, true +} + +func (q *Queue[T]) PeekFront() (T, bool) { + var t T + + if q.IsEmpty() { + return t, false + } + + t = q.head.value + return t, true +} + +func (q *Queue[T]) List() []T { + if q.IsEmpty() { + return nil + } + + vals := make([]T, 0, q.size) + + curr := q.head + for curr != nil { + vals = append(vals, curr.value) + curr = curr.next + } + + return vals +} + +func (q *Queue[T]) IsEmpty() bool { + return q.size == 0 +} + +func (q *Queue[T]) Size() uint { + return q.size +} diff --git a/pkg/ds/queue/queue_test.go b/pkg/ds/queue/queue_test.go new file mode 100644 index 0000000..9537f04 --- /dev/null +++ b/pkg/ds/queue/queue_test.go @@ -0,0 +1,153 @@ +package queue_test + +import ( + "slices" + "testing" + + "github.com/KyberNetwork/tradinglib/pkg/ds/queue" + "github.com/stretchr/testify/assert" +) + +var vals = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} //nolint: gochecknoglobals + +func TestPushBack(t *testing.T) { + q := queue.New[int]() + for _, v := range vals { + q.PushBack(v) + } + qVals := q.List() + assert.Equal(t, vals, qVals) +} + +func TestPushFront(t *testing.T) { + q := queue.New[int]() + for _, v := range vals { + q.PushFront(v) + } + qVals := q.List() + + reverse := make([]int, len(vals)) + copy(reverse, vals) + slices.Reverse(reverse) + assert.Equal(t, reverse, qVals) +} + +func TestPopBack(t *testing.T) { + testVals := make([]int, len(vals)) + copy(testVals, vals) + q := queue.New[int]() + for _, v := range vals { + q.PushBack(v) + } + + for { + v, ok := q.PopBack() + if !ok { + break + } + testVal := testVals[len(testVals)-1] + assert.Equal(t, testVal, v) + + testVals = testVals[:len(testVals)-1] + assert.Equal(t, len(testVals), int((q.Size()))) + if len(testVals) == 0 { + assert.Equal(t, 0, len(q.List())) + break + } + assert.Equal(t, testVals, q.List()) + } +} + +func TestPopFront(t *testing.T) { + testVals := make([]int, len(vals)) + copy(testVals, vals) + + q := queue.New[int]() + for _, v := range vals { + q.PushBack(v) + } + + for { + v, ok := q.PopFront() + if !ok { + break + } + testVal := testVals[0] + assert.Equal(t, testVal, v) + + testVals = testVals[1:] + assert.Equal(t, len(testVals), int((q.Size()))) + if len(testVals) == 0 { + assert.Equal(t, 0, len(q.List())) + break + } + assert.Equal(t, q.List(), testVals) + } +} + +func TestQueue(t *testing.T) { + // random + q := queue.New[int]() + + q.PushBack(1) + assert.Equal(t, []int{1}, q.List()) + + q.PushFront(2) + assert.Equal(t, []int{2, 1}, q.List()) + + q.PushBack(3) + assert.Equal(t, []int{2, 1, 3}, q.List()) + + q.PushBack(4) + assert.Equal(t, []int{2, 1, 3, 4}, q.List()) + + q.PushFront(5) + assert.Equal(t, []int{5, 2, 1, 3, 4}, q.List()) + + assert.Equal(t, 5, int(q.Size())) + + v, ok := q.PopBack() + assert.True(t, ok) + assert.Equal(t, 4, v) + assert.Equal(t, []int{5, 2, 1, 3}, q.List()) + assert.Equal(t, 4, int(q.Size())) + v, ok = q.PeekFront() + assert.True(t, ok) + assert.Equal(t, 5, v) + v, ok = q.PeekBack() + assert.True(t, ok) + assert.Equal(t, 3, v) + + v, ok = q.PopFront() + assert.True(t, ok) + assert.Equal(t, 5, v) + assert.Equal(t, []int{2, 1, 3}, q.List()) + assert.Equal(t, 3, int(q.Size())) + v, ok = q.PeekFront() + assert.True(t, ok) + assert.Equal(t, 2, v) + v, ok = q.PeekBack() + assert.True(t, ok) + assert.Equal(t, 3, v) + + q.PushFront(6) + assert.Equal(t, []int{6, 2, 1, 3}, q.List()) + + for i := 0; i < 20; i++ { + q.PopBack() + } + v, ok = q.PopBack() + assert.False(t, ok) + assert.Equal(t, 0, v) // default int + assert.True(t, q.IsEmpty()) + + v, ok = q.PopFront() + assert.False(t, ok) + assert.Equal(t, 0, v) // default int + assert.True(t, q.IsEmpty()) + + _, ok = q.PeekFront() + assert.False(t, ok) + _, ok = q.PeekBack() + assert.False(t, ok) +} diff --git a/pkg/httpclient/query.go b/pkg/httpclient/query.go index 70b62cc..0429c46 100644 --- a/pkg/httpclient/query.go +++ b/pkg/httpclient/query.go @@ -54,7 +54,7 @@ func (q Query) url() url.Values { return (url.Values)(q) } -func (q Query) setString(key, value string, options []string) Query { +func (q Query) setString(key, value string, options []string) Query { //nolint: unparam if value == "" && isOmitEmpty(options) { return q } diff --git a/x/syncmap/syncmap_test.go b/x/syncmap/syncmap_test.go index a9e0773..23584e4 100644 --- a/x/syncmap/syncmap_test.go +++ b/x/syncmap/syncmap_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/KyberNetwork/tradinglib/x/syncmap" + "github.com/sourcegraph/conc/pool" "github.com/stretchr/testify/require" - "golang.org/x/sync/errgroup" ) func TestSyncMap(t *testing.T) { @@ -27,29 +27,29 @@ func TestSyncMap(t *testing.T) { } sm := syncmap.New[string, int]() - errGr := errgroup.Group{} + p := pool.New().WithErrors() - errGr.Go(func() error { + p.Go(func() error { for i := range tests { sm.Store(tests[i].k, tests[i].v) } return nil }) - errGr.Go(func() error { + p.Go(func() error { for i := range tests { sm.Store(tests[i].k, tests[i].v) } return nil }) - errGr.Go(func() error { + p.Go(func() error { sm.Load(strconv.Itoa(rand.Intn(testRange))) // nolint: gosec return nil }) - errGr.Go(func() error { + p.Go(func() error { sm.Delete(strconv.Itoa(rand.Intn(testRange))) // nolint: gosec return nil }) - errGr.Go(func() error { + p.Go(func() error { for i := range tests { _, err := sm.Update(tests[i].k, func(v int) (int, error) { if v < 5 { @@ -61,5 +61,5 @@ func TestSyncMap(t *testing.T) { } return nil }) - _ = errGr.Wait() + _ = p.Wait() }