diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index a0da047..896f6a6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go-version: [ '1.18', '1.19.x' ] + go-version: [ '1.21', '1.21.x' ] steps: - name: Checkout the repository @@ -26,7 +26,7 @@ jobs: run: go version - name: Install staticcheck - run: go install honnef.co/go/tools/cmd/staticcheck@2022.1.3 + run: go install honnef.co/go/tools/cmd/staticcheck@latest - name: Install cover run: go get -u golang.org/x/tools/cmd/cover @@ -38,7 +38,7 @@ jobs: run: go get -u github.com/mattn/goveralls - name: Staticcheck - run: staticcheck -checks="all,-ST1000" github.com/msales/gox/... + run: staticcheck -checks="all,-ST1000,-SA1019" github.com/msales/gox/... - name: Vet run: go vet ./... diff --git a/go.mod b/go.mod index 44f6787..4d11202 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/msales/gox -go 1.16 +go 1.21 diff --git a/queuex/fifo.go b/queuex/fifo.go new file mode 100644 index 0000000..2456298 --- /dev/null +++ b/queuex/fifo.go @@ -0,0 +1,48 @@ +package queuex + +import "sync" + +// FIFO is simple implementation of FIFO (first-in-first-out) queue with locks. +type FIFO[T any] struct { + m sync.Mutex + + vals []T +} + +// NewFIFO initializes queue with values. +func NewFIFO[T any](vals ...T) *FIFO[T] { + return &FIFO[T]{vals: vals} +} + +// Push adds elements at the end of the queue. +func (f *FIFO[T]) Push(v T) *FIFO[T] { + f.m.Lock() + defer f.m.Unlock() + + f.vals = append(f.vals, v) + return f +} + +// Pop returns first element from the queue. +// Bool is also returned to indicate if queue has run out of the values. +func (f *FIFO[T]) Pop() (T, bool) { + f.m.Lock() + defer f.m.Unlock() + + if len(f.vals) == 0 { + var t T + return t, false + } + + v := f.vals[0] + f.vals = f.vals[1:] + return v, true +} + +// Len returns length of queue. +func (f *FIFO[T]) Len() int { + f.m.Lock() + defer f.m.Unlock() + + return len(f.vals) +} diff --git a/queuex/fifo_test.go b/queuex/fifo_test.go new file mode 100644 index 0000000..78c48b0 --- /dev/null +++ b/queuex/fifo_test.go @@ -0,0 +1,146 @@ +package queuex_test + +import ( + "github.com/msales/gox/queuex" + "testing" +) + +func TestFIFO(t *testing.T) { + queue := queuex.NewFIFO([]int{ + 1, 2, + }...) + + queue.Push(3) + + got, ok := queue.Pop() + wantVal := 1 + wantOK := true + if wantVal != got { + t.Errorf("Got %+v, want %+v", got, wantVal) + } + + if ok != wantOK { + t.Errorf("Got %+v, want %+v", ok, wantOK) + } + + got, ok = queue.Pop() + wantVal = 2 + wantOK = true + if wantVal != got { + t.Errorf("Got %+v, want %+v", got, wantVal) + } + + if ok != wantOK { + t.Errorf("Got %+v, want %+v", ok, wantOK) + } + + got, ok = queue.Pop() + wantVal = 3 + wantOK = true + if wantVal != got { + t.Errorf("Got %+v, want %+v", got, wantVal) + } + + if ok != wantOK { + t.Errorf("Got %+v, want %+v", ok, wantOK) + } + + got, ok = queue.Pop() + wantVal = 0 + wantOK = false + if wantVal != got { + t.Errorf("Got %+v, want %+v", got, wantVal) + } + + if ok != wantOK { + t.Errorf("Got %+v, want %+v", ok, wantOK) + } +} + +func TestFIFO_EmptyQueue(t *testing.T) { + queue := queuex.NewFIFO[int]() + + got, ok := queue.Pop() + wantVal := 0 + wantOK := false + if wantVal != got { + t.Errorf("Got %+v, want %+v", got, wantVal) + } + + if ok != wantOK { + t.Errorf("Got %+v, want %+v", ok, wantOK) + } + + got, ok = queue.Pop() + if wantVal != got { + t.Errorf("Got %+v, want %+v", got, wantVal) + } + + if ok != wantOK { + t.Errorf("Got %+v, want %+v", ok, wantOK) + } +} + +func BenchmarkFIFO_Pop(b *testing.B) { + vals := make([]int, 1000) + for i := 0; i < 1000; i++ { + vals[i] = i + } + + queue := queuex.NewFIFO(vals...) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + queue.Pop() + } +} + +func BenchmarkFIFO_Push(b *testing.B) { + vals := make([]int, 1000) + for i := 0; i < 1000; i++ { + vals[i] = i + } + + queue := queuex.NewFIFO(vals...) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + queue.Push(i) + } +} + +func BenchmarkFIFO_EmptyPush(b *testing.B) { + queue := queuex.NewFIFO[int]() + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + queue.Push(i) + } +} + +func BenchmarkFIFO_RandomPushPop(b *testing.B) { + vals := make([]int, 1000) + for i := 0; i < 1000; i++ { + vals[i] = i + } + + queue := queuex.NewFIFO(vals...) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + if i%2 == 0 { + queue.Pop() + continue + } else { + queue.Push(i) + } + } +}