Rename package circ to queue.
This commit is contained in:
parent
b00fe25128
commit
f7474fb673
70
circ/lib.go
70
circ/lib.go
@ -1,70 +0,0 @@
|
|||||||
// Package circ implements a circular FIFO buffer.
|
|
||||||
package circ
|
|
||||||
|
|
||||||
// B is a circular FIFO buffer.
|
|
||||||
type B[T any] struct {
|
|
||||||
buf []T
|
|
||||||
len int
|
|
||||||
head int
|
|
||||||
tail int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make creates a new buffer.
|
|
||||||
func Make[T any](cap int) B[T] {
|
|
||||||
buf := make([]T, cap)
|
|
||||||
return B[T]{buf: buf}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capacity returns the total capacity of the buffer.
|
|
||||||
func (b *B[T]) Capacity() int {
|
|
||||||
return cap(b.buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of items in the buffer.
|
|
||||||
func (b *B[T]) Len() int {
|
|
||||||
return b.len
|
|
||||||
}
|
|
||||||
|
|
||||||
// CanPush returns true if the buffer has space for new items.
|
|
||||||
func (b *B[T]) CanPush() bool {
|
|
||||||
return cap(b.buf)-b.len != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// CanPop returns true if the buffer has one or more items.
|
|
||||||
func (b *B[T]) CanPop() bool {
|
|
||||||
return b.len != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// PopFront returns the front-most item from the buffer.
|
|
||||||
//
|
|
||||||
// If the buffer is empty, it panics.
|
|
||||||
func (b *B[T]) PopFront() T {
|
|
||||||
if !b.CanPop() {
|
|
||||||
panic("cannot pop from empty buffer")
|
|
||||||
}
|
|
||||||
item := b.buf[b.head]
|
|
||||||
// clear buffer slot so as not to hold on to garbage
|
|
||||||
var empty T
|
|
||||||
b.buf[b.head] = empty
|
|
||||||
b.len--
|
|
||||||
b.head++
|
|
||||||
if b.head == cap(b.buf) {
|
|
||||||
b.head = 0
|
|
||||||
}
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
|
|
||||||
// PushBack adds an item to the end of the buffer.
|
|
||||||
//
|
|
||||||
// If the buffer is full, it panics.
|
|
||||||
func (b *B[T]) PushBack(value T) {
|
|
||||||
if !b.CanPush() {
|
|
||||||
panic("cannot push back to full buffer")
|
|
||||||
}
|
|
||||||
b.buf[b.tail] = value
|
|
||||||
b.len++
|
|
||||||
b.tail++
|
|
||||||
if b.tail == cap(b.buf) {
|
|
||||||
b.tail = 0
|
|
||||||
}
|
|
||||||
}
|
|
14
mq/lib.go
14
mq/lib.go
@ -31,7 +31,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"gogs.humancabbage.net/sam/priorityq"
|
"gogs.humancabbage.net/sam/priorityq"
|
||||||
"gogs.humancabbage.net/sam/priorityq/circ"
|
"gogs.humancabbage.net/sam/priorityq/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Q is a concurrent, dual-priority message queue.
|
// Q is a concurrent, dual-priority message queue.
|
||||||
@ -41,8 +41,8 @@ type Q[T any] struct {
|
|||||||
|
|
||||||
// Make a new queue.
|
// Make a new queue.
|
||||||
func Make[T any](cap int) Q[T] {
|
func Make[T any](cap int) Q[T] {
|
||||||
high := circ.Make[T](cap)
|
high := queue.Make[T](cap)
|
||||||
low := circ.Make[T](cap)
|
low := queue.Make[T](cap)
|
||||||
s := &state[T]{
|
s := &state[T]{
|
||||||
high: high,
|
high: high,
|
||||||
low: low,
|
low: low,
|
||||||
@ -55,8 +55,8 @@ func Make[T any](cap int) Q[T] {
|
|||||||
|
|
||||||
type state[T any] struct {
|
type state[T any] struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
high circ.B[T]
|
high queue.Q[T]
|
||||||
low circ.B[T]
|
low queue.Q[T]
|
||||||
canSendHigh sync.Cond
|
canSendHigh sync.Cond
|
||||||
canSendLow sync.Cond
|
canSendLow sync.Cond
|
||||||
canRecv sync.Cond
|
canRecv sync.Cond
|
||||||
@ -163,7 +163,7 @@ func (s *state[T]) TrySendLow(value T) error {
|
|||||||
return s.trySend(value, &s.low)
|
return s.trySend(value, &s.low)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state[T]) send(value T, buf *circ.B[T], cond *sync.Cond) {
|
func (s *state[T]) send(value T, buf *queue.Q[T], cond *sync.Cond) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
for {
|
for {
|
||||||
@ -181,7 +181,7 @@ func (s *state[T]) send(value T, buf *circ.B[T], cond *sync.Cond) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *state[T]) trySend(value T, buf *circ.B[T]) error {
|
func (s *state[T]) trySend(value T, buf *queue.Q[T]) error {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.closed {
|
if s.closed {
|
||||||
|
75
queue/lib.go
Normal file
75
queue/lib.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Package q implements a non-concurrent queue.
|
||||||
|
//
|
||||||
|
// # Implementation
|
||||||
|
//
|
||||||
|
// [Q] is a classic ring buffer. It tracks its head, tail, and length. This
|
||||||
|
// makes determining whether the queue is full or empty trivial.
|
||||||
|
package queue
|
||||||
|
|
||||||
|
// Q is a non-concurrent queue.
|
||||||
|
type Q[T any] struct {
|
||||||
|
buf []T
|
||||||
|
len int
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make creates a new queue.
|
||||||
|
func Make[T any](cap int) Q[T] {
|
||||||
|
buf := make([]T, cap)
|
||||||
|
return Q[T]{buf: buf}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capacity returns the total capacity of the queue.
|
||||||
|
func (b *Q[T]) Capacity() int {
|
||||||
|
return cap(b.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of items in the queue.
|
||||||
|
func (b *Q[T]) Len() int {
|
||||||
|
return b.len
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanPush returns true if the queue has space for new items.
|
||||||
|
func (b *Q[T]) CanPush() bool {
|
||||||
|
return cap(b.buf)-b.len != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanPop returns true if the queue has one or more items.
|
||||||
|
func (b *Q[T]) CanPop() bool {
|
||||||
|
return b.len != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PopFront returns the front-most item from the queue.
|
||||||
|
//
|
||||||
|
// If the queue is empty, it panics.
|
||||||
|
func (b *Q[T]) PopFront() T {
|
||||||
|
if !b.CanPop() {
|
||||||
|
panic("cannot pop from empty queue")
|
||||||
|
}
|
||||||
|
item := b.buf[b.head]
|
||||||
|
// clear queue slot so as not to hold on to garbage
|
||||||
|
var empty T
|
||||||
|
b.buf[b.head] = empty
|
||||||
|
b.len--
|
||||||
|
b.head++
|
||||||
|
if b.head == cap(b.buf) {
|
||||||
|
b.head = 0
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
// PushBack adds an item to the end of the queue.
|
||||||
|
//
|
||||||
|
// If the queue is full, it panics.
|
||||||
|
func (b *Q[T]) PushBack(value T) {
|
||||||
|
if !b.CanPush() {
|
||||||
|
panic("cannot push back to full queue")
|
||||||
|
}
|
||||||
|
b.buf[b.tail] = value
|
||||||
|
b.len++
|
||||||
|
b.tail++
|
||||||
|
if b.tail == cap(b.buf) {
|
||||||
|
b.tail = 0
|
||||||
|
}
|
||||||
|
}
|
@ -1,28 +1,28 @@
|
|||||||
package circ_test
|
package queue_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gogs.humancabbage.net/sam/priorityq/circ"
|
"gogs.humancabbage.net/sam/priorityq/queue"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRepeatPushPop(t *testing.T) {
|
func TestRepeatPushPop(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
cb := circ.Make[int](4)
|
q := queue.Make[int](4)
|
||||||
if cb.Capacity() != 4 {
|
if q.Capacity() != 4 {
|
||||||
t.Errorf("wrong capacity")
|
t.Errorf("wrong capacity")
|
||||||
}
|
}
|
||||||
for i := 0; i < 50; i++ {
|
for i := 0; i < 50; i++ {
|
||||||
cb.PushBack(1)
|
q.PushBack(1)
|
||||||
cb.PushBack(2)
|
q.PushBack(2)
|
||||||
cb.PushBack(3)
|
q.PushBack(3)
|
||||||
cb.PushBack(4)
|
q.PushBack(4)
|
||||||
if cb.Len() != 4 {
|
if q.Len() != 4 {
|
||||||
t.Errorf("wrong length")
|
t.Errorf("wrong length")
|
||||||
}
|
}
|
||||||
checkPop := func(n int) {
|
checkPop := func(n int) {
|
||||||
if v := cb.PopFront(); v != n {
|
if v := q.PopFront(); v != n {
|
||||||
t.Errorf("popped %d, expected %d", v, n)
|
t.Errorf("popped %d, expected %d", v, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,18 +35,18 @@ func TestRepeatPushPop(t *testing.T) {
|
|||||||
|
|
||||||
func TestInterleavedPushPop(t *testing.T) {
|
func TestInterleavedPushPop(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
cb := circ.Make[int](4)
|
q := queue.Make[int](4)
|
||||||
checkPop := func(n int) {
|
checkPop := func(n int) {
|
||||||
if v := cb.PopFront(); v != n {
|
if v := q.PopFront(); v != n {
|
||||||
t.Errorf("popped %d, expected %d", v, n)
|
t.Errorf("popped %d, expected %d", v, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cb.PushBack(1)
|
q.PushBack(1)
|
||||||
cb.PushBack(2)
|
q.PushBack(2)
|
||||||
cb.PushBack(3)
|
q.PushBack(3)
|
||||||
checkPop(1)
|
checkPop(1)
|
||||||
cb.PushBack(4)
|
q.PushBack(4)
|
||||||
cb.PushBack(5)
|
q.PushBack(5)
|
||||||
checkPop(2)
|
checkPop(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +57,8 @@ func TestEmptyPopPanic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
cb := circ.Make[int](4)
|
q := queue.Make[int](4)
|
||||||
cb.PopFront()
|
q.PopFront()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFullPushPanic(t *testing.T) {
|
func TestFullPushPanic(t *testing.T) {
|
||||||
@ -68,13 +68,13 @@ func TestFullPushPanic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
cb := circ.Make[int](1)
|
q := queue.Make[int](1)
|
||||||
cb.PushBack(1)
|
q.PushBack(1)
|
||||||
cb.PushBack(2)
|
q.PushBack(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPush(b *testing.B) {
|
func BenchmarkPush(b *testing.B) {
|
||||||
cb := circ.Make[int](b.N)
|
q := queue.Make[int](b.N)
|
||||||
rs := rand.NewSource(0)
|
rs := rand.NewSource(0)
|
||||||
r := rand.New(rs)
|
r := rand.New(rs)
|
||||||
items := make([]int, b.N)
|
items := make([]int, b.N)
|
||||||
@ -83,19 +83,19 @@ func BenchmarkPush(b *testing.B) {
|
|||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
cb.PushBack(items[i])
|
q.PushBack(items[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkPop(b *testing.B) {
|
func BenchmarkPop(b *testing.B) {
|
||||||
cb := circ.Make[int](b.N)
|
q := queue.Make[int](b.N)
|
||||||
rs := rand.NewSource(0)
|
rs := rand.NewSource(0)
|
||||||
r := rand.New(rs)
|
r := rand.New(rs)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
cb.PushBack(r.Int())
|
q.PushBack(r.Int())
|
||||||
}
|
}
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
cb.PopFront()
|
q.PopFront()
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user