Initial commit.
This commit is contained in:
59
circ/lib.go
Normal file
59
circ/lib.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package circ
|
||||
|
||||
// B is a generic, non-concurrent circular FIFO buffer.
|
||||
type B[T any] struct {
|
||||
buf []T
|
||||
len int
|
||||
head int
|
||||
tail int
|
||||
}
|
||||
|
||||
// Make creates a new circular buffer.
|
||||
func Make[T any](cap int) B[T] {
|
||||
buf := make([]T, cap)
|
||||
return B[T]{buf: buf}
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
var empty T
|
||||
item := b.buf[b.head]
|
||||
// clear buffer slot so that we don't hold on to garbage
|
||||
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
|
||||
}
|
||||
}
|
67
circ/lib_test.go
Normal file
67
circ/lib_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package circ_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gogs.humancabbage.net/sam/priorityq/circ"
|
||||
)
|
||||
|
||||
func TestRepeatPushPop(t *testing.T) {
|
||||
t.Parallel()
|
||||
cb := circ.Make[int](4)
|
||||
for i := 0; i < 50; i++ {
|
||||
cb.PushBack(1)
|
||||
cb.PushBack(2)
|
||||
cb.PushBack(3)
|
||||
cb.PushBack(4)
|
||||
checkPop := func(n int) {
|
||||
if v := cb.PopFront(); v != n {
|
||||
t.Errorf("popped %d, expected %d", v, n)
|
||||
}
|
||||
}
|
||||
checkPop(1)
|
||||
checkPop(2)
|
||||
checkPop(3)
|
||||
checkPop(4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterleavedPushPop(t *testing.T) {
|
||||
t.Parallel()
|
||||
cb := circ.Make[int](4)
|
||||
checkPop := func(n int) {
|
||||
if v := cb.PopFront(); v != n {
|
||||
t.Errorf("popped %d, expected %d", v, n)
|
||||
}
|
||||
}
|
||||
cb.PushBack(1)
|
||||
cb.PushBack(2)
|
||||
cb.PushBack(3)
|
||||
checkPop(1)
|
||||
cb.PushBack(4)
|
||||
cb.PushBack(5)
|
||||
checkPop(2)
|
||||
}
|
||||
|
||||
func TestEmptyPopPanic(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("popping empty buffer did not panic")
|
||||
}
|
||||
}()
|
||||
t.Parallel()
|
||||
cb := circ.Make[int](4)
|
||||
cb.PopFront()
|
||||
}
|
||||
|
||||
func TestFullPushPanic(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("pushing full buffer did not panic")
|
||||
}
|
||||
}()
|
||||
t.Parallel()
|
||||
cb := circ.Make[int](1)
|
||||
cb.PushBack(1)
|
||||
cb.PushBack(2)
|
||||
}
|
Reference in New Issue
Block a user