Initial commit.

This commit is contained in:
2023-02-28 20:33:22 -08:00
commit 4052afa31d
8 changed files with 576 additions and 0 deletions

59
circ/lib.go Normal file
View 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
View 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)
}