Files
go-ringdeque/ring.go

165 lines
3.0 KiB
Go

package ring
import (
"iter"
)
// A Ring is a deque, implemented using a circular buffer.
type Ring[T any] struct {
data []T // Is not 'nil'.
growthFactor uint64 // Is greater than 1.
size uint64 // Is not greater than 'len(data)'.
offset uint64 // Is not greater than 'size'.
}
func (r Ring[T]) Cap() int {
return len(r.data)
}
func (r Ring[T]) Len() int {
return int(r.size)
}
// fetch returns the value of a certain index. The index MUST NOT be out of
// range.
func (r Ring[T]) fetch(index uint64) T {
if index >= r.size {
panic("Out of range!")
}
return r.data[(r.offset+index)%r.size]
}
func (r Ring[T]) Get(index int) T {
return r.fetch(uint64(index))
}
func (r Ring[T]) Front() T {
return r.fetch(0)
}
func (r Ring[T]) Back() T {
return r.fetch(r.size - 1)
}
// resize replaces the current data table with a new table a new 'capacity'. The
// new capacity MUST NOT be less than [Ring.Len].
func (r *Ring[T]) resize(capacity uint64) {
if capacity < r.size {
panic("Resize too small!")
}
newData := make([]T, capacity)
for i, value := range r.Entries() {
newData[i] = value
}
r.offset = 0
r.data = newData
}
// grow increases the size of the array by its growth factor. It is guaranteed
// to not be [Ring.full].
func (r *Ring[T]) grow() {
if r.size == 0 {
r.resize(1)
} else {
r.resize(r.size * r.growthFactor)
}
}
// full returns true if every slot in the data table is full.
func (r Ring[T]) full() bool {
return r.size == uint64(len(r.data))
}
// shrink decreases the size of the data table by its growth factor. Do not call
// this when the data table is not [Ring.sparse].
func (r *Ring[T]) shrink() {
r.resize(r.size / r.growthFactor)
}
// space returns true if the data table is empty enough to shrink.
func (r *Ring[T]) sparse() bool {
return r.size*r.growthFactor < uint64(len(r.data))
}
// set updates the value of a certain index. The index MUST be in range.
func (r Ring[T]) set(index uint64, value T) {
if index >= r.size {
panic("Out of range!")
}
r.data[(r.offset+index)%r.size] = value
}
func (r *Ring[T]) PushBack(value T) {
if r.full() {
r.grow()
}
r.size++
r.set(r.size-1, value)
}
func (r *Ring[T]) PopBack() T {
value := r.fetch(r.size - 1)
r.size--
if r.sparse() {
r.shrink()
}
return value
}
func (r *Ring[T]) PushFront(value T) {
if r.full() {
r.grow()
}
r.offset--
r.size++
r.set(0, value)
}
func (r *Ring[T]) PopFront() T {
value := r.fetch(0)
r.size--
r.offset++
if r.sparse() {
r.shrink()
}
return value
}
func (r Ring[T]) Entries() iter.Seq2[int, T] {
return func(yield func(int, T) bool) {
for i := range r.size {
if !yield(int(i), r.fetch(i)) {
return
}
}
}
}
func New[T any](options ...Option) *Ring[T] {
config := &config{
growthFactor: DefaultCapacity,
capacity: DefaultCapacity,
}
for _, option := range options {
option(config)
}
return &Ring[T]{
data: make([]T, config.capacity),
offset: 0,
size: 0,
growthFactor: config.growthFactor,
}
}