This commit is contained in:
		
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| module gogs.humancabbage.net/sam/priorityq | module gogs.humancabbage.net/sam/priorityq | ||||||
|  |  | ||||||
| go 1.20 | go 1.23 | ||||||
|  |  | ||||||
| require golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 | require golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								pq/lib.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								pq/lib.go
									
									
									
									
									
								
							| @@ -24,6 +24,7 @@ | |||||||
| package pq | package pq | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"iter" | ||||||
| 	"sync" | 	"sync" | ||||||
|  |  | ||||||
| 	"gogs.humancabbage.net/sam/priorityq" | 	"gogs.humancabbage.net/sam/priorityq" | ||||||
| @@ -153,3 +154,18 @@ func (s *state[P, T]) TrySend(priority P, value T) error { | |||||||
| 	s.canRecv.Broadcast() | 	s.canRecv.Broadcast() | ||||||
| 	return nil | 	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() | 	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) { | func BenchmarkSend(b *testing.B) { | ||||||
| 	q := pq.Make[int, int](b.N) | 	q := pq.Make[int, int](b.N) | ||||||
| 	// randomize priorities to get amortized cost per op | 	// 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. | // makes determining whether the queue is full or empty trivial. | ||||||
| package queue | package queue | ||||||
|  |  | ||||||
|  | import "iter" | ||||||
|  |  | ||||||
| // Q is a non-concurrent queue. | // Q is a non-concurrent queue. | ||||||
| type Q[T any] struct { | type Q[T any] struct { | ||||||
| 	buf  []T | 	buf  []T | ||||||
| @@ -73,3 +75,32 @@ func (b *Q[T]) PushBack(value T) { | |||||||
| 		b.tail = 0 | 		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) | 	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) { | func BenchmarkPush(b *testing.B) { | ||||||
| 	q := queue.Make[int](b.N) | 	q := queue.Make[int](b.N) | ||||||
| 	rs := rand.NewSource(0) | 	rs := rand.NewSource(0) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user