Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
9a15ebed25 | |||
c5dde2ab43 | |||
38ef55e463 | |||
87212466ae |
@ -17,12 +17,12 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21.5
|
||||
go-version: 1.23.0
|
||||
-
|
||||
name: Run linter
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.54
|
||||
version: v1.60.2
|
||||
-
|
||||
name: Run tests
|
||||
run: go test ./...
|
||||
|
2
go.mod
2
go.mod
@ -1,5 +1,5 @@
|
||||
module gogs.humancabbage.net/sam/priorityq
|
||||
|
||||
go 1.20
|
||||
go 1.23
|
||||
|
||||
require golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
|
||||
|
16
mq/lib.go
16
mq/lib.go
@ -28,6 +28,7 @@
|
||||
package mq
|
||||
|
||||
import (
|
||||
"iter"
|
||||
"sync"
|
||||
|
||||
"gogs.humancabbage.net/sam/priorityq"
|
||||
@ -199,3 +200,18 @@ func (s *state[T]) trySend(value T, buf *queue.Q[T]) error {
|
||||
s.canRecv.Broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Iter returns an iterator that consumes values until the queue is closed.
|
||||
func (s *state[T]) Iter() iter.Seq[T] {
|
||||
return func(yield func(T) bool) {
|
||||
for {
|
||||
t, ok := s.Recv()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,6 +164,30 @@ func TestConcProducerConsumer(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestIter(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := mq.Make[int](4)
|
||||
q.Send(1)
|
||||
q.Send(2)
|
||||
q.Send(3)
|
||||
q.SendHigh(0)
|
||||
q.Close()
|
||||
i := 0
|
||||
for v := range q.Iter() {
|
||||
if v != i {
|
||||
t.Errorf("expected %d, got %d", i, v)
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// to test yield() returning false
|
||||
q = mq.Make[int](4)
|
||||
q.Send(3)
|
||||
for _ = range q.Iter() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSend(b *testing.B) {
|
||||
q := mq.Make[int](b.N)
|
||||
b.ResetTimer()
|
||||
|
16
npq/lib.go
16
npq/lib.go
@ -38,6 +38,7 @@ package npq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"sync"
|
||||
|
||||
"gogs.humancabbage.net/sam/priorityq"
|
||||
@ -212,3 +213,18 @@ func (s *state[P, T]) validatePriority(priority P) {
|
||||
priority, s.min, s.max))
|
||||
}
|
||||
}
|
||||
|
||||
// Iter returns an iterator that consumes values until the queue is closed.
|
||||
func (s *state[P, T]) Iter() iter.Seq[T] {
|
||||
return func(yield func(T) bool) {
|
||||
for {
|
||||
t, ok := s.Recv()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,6 +186,30 @@ func TestConcProducerConsumer(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestIter(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := npq.Make[int, int](4, 0, 16)
|
||||
q.Send(4, 0)
|
||||
q.Send(3, 1)
|
||||
q.Send(2, 2)
|
||||
q.Send(1, 3)
|
||||
q.Close()
|
||||
i := 0
|
||||
for v := range q.Iter() {
|
||||
if v != i {
|
||||
t.Errorf("expected %d, got %d", i, v)
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// to test yield() returning false
|
||||
q = npq.Make[int, int](4, 0, 16)
|
||||
q.Send(1, 3)
|
||||
for _ = range q.Iter() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const highPriority = 2
|
||||
|
||||
func BenchmarkSend(b *testing.B) {
|
||||
|
16
pq/lib.go
16
pq/lib.go
@ -24,6 +24,7 @@
|
||||
package pq
|
||||
|
||||
import (
|
||||
"iter"
|
||||
"sync"
|
||||
|
||||
"gogs.humancabbage.net/sam/priorityq"
|
||||
@ -153,3 +154,18 @@ func (s *state[P, T]) TrySend(priority P, value T) error {
|
||||
s.canRecv.Broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Iter returns an iterator that consumes values until the queue is closed.
|
||||
func (s *state[P, T]) Iter() iter.Seq[T] {
|
||||
return func(yield func(T) bool) {
|
||||
for {
|
||||
_, t, ok := s.Recv()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !yield(t) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +147,30 @@ func TestConcProducerConsumer(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestIter(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := pq.Make[int, int](4)
|
||||
q.Send(4, 0)
|
||||
q.Send(3, 1)
|
||||
q.Send(2, 2)
|
||||
q.Send(1, 3)
|
||||
q.Close()
|
||||
i := 0
|
||||
for v := range q.Iter() {
|
||||
if v != i {
|
||||
t.Errorf("expected %d, got %d", i, v)
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
// to test yield() returning false
|
||||
q = pq.Make[int, int](4)
|
||||
q.Send(1, 3)
|
||||
for _ = range q.Iter() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSend(b *testing.B) {
|
||||
q := pq.Make[int, int](b.N)
|
||||
// randomize priorities to get amortized cost per op
|
||||
|
31
queue/lib.go
31
queue/lib.go
@ -6,6 +6,8 @@
|
||||
// makes determining whether the queue is full or empty trivial.
|
||||
package queue
|
||||
|
||||
import "iter"
|
||||
|
||||
// Q is a non-concurrent queue.
|
||||
type Q[T any] struct {
|
||||
buf []T
|
||||
@ -73,3 +75,32 @@ func (b *Q[T]) PushBack(value T) {
|
||||
b.tail = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Iter returns an iterator over all items in the queue.
|
||||
//
|
||||
// This does not pop items off the queue.
|
||||
func (b *Q[T]) Iter() iter.Seq[T] {
|
||||
return func(yield func(T) bool) {
|
||||
remaining := b.len
|
||||
i := b.head
|
||||
for remaining > 0 {
|
||||
yield(b.buf[i])
|
||||
remaining--
|
||||
i++
|
||||
if i == b.len {
|
||||
i = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterPop returns an iterator that pops each item off the queue.
|
||||
func (b *Q[T]) IterPop() iter.Seq[T] {
|
||||
return func(yield func(T) bool) {
|
||||
for b.CanPop() {
|
||||
if !yield(b.PopFront()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,51 @@ func TestFullPushPanic(t *testing.T) {
|
||||
q.PushBack(2)
|
||||
}
|
||||
|
||||
func TestIter(t *testing.T) {
|
||||
q := queue.Make[int](4)
|
||||
q.PushBack(1)
|
||||
q.PushBack(2)
|
||||
q.PushBack(3)
|
||||
q.PushBack(4)
|
||||
i := 0
|
||||
for v := range q.Iter() {
|
||||
expected := i + 1
|
||||
if v != expected {
|
||||
t.Errorf("iter %d should have value %d, not %d",
|
||||
i, expected, v)
|
||||
}
|
||||
i++
|
||||
}
|
||||
if q.Len() != 4 {
|
||||
t.Errorf("wrong length")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIterPop(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := queue.Make[int](4)
|
||||
q.PushBack(1)
|
||||
q.PushBack(2)
|
||||
q.PushBack(3)
|
||||
q.PushBack(4)
|
||||
i := 0
|
||||
for v := range q.IterPop() {
|
||||
expected := i + 1
|
||||
if v != expected {
|
||||
t.Errorf("iter %d should have value %d, not %d",
|
||||
i, expected, v)
|
||||
}
|
||||
i++
|
||||
// to test yield() returning false
|
||||
if i == 4 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if q.Len() != 0 {
|
||||
t.Errorf("wrong length")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPush(b *testing.B) {
|
||||
q := queue.Make[int](b.N)
|
||||
rs := rand.NewSource(0)
|
||||
|
Loading…
Reference in New Issue
Block a user