package dataext import "iter" type RingBuffer[T any] struct { items []T // capacity int // max number of items the buffer can hold size int // how many items are in the buffer head int // ptr to next item } func NewRingBuffer[T any](capacity int) *RingBuffer[T] { return &RingBuffer[T]{ items: make([]T, capacity), capacity: capacity, size: 0, head: 0, } } func (rb *RingBuffer[T]) Push(item T) { if rb.size < rb.capacity { rb.size++ } rb.items[rb.head] = item rb.head = (rb.head + 1) % rb.capacity } func (rb *RingBuffer[T]) PushPop(item T) *T { if rb.size < rb.capacity { rb.size++ rb.items[rb.head] = item rb.head = (rb.head + 1) % rb.capacity return nil } else { prev := rb.items[rb.head] rb.items[rb.head] = item rb.head = (rb.head + 1) % rb.capacity return &prev } } func (rb *RingBuffer[T]) Peek() (T, bool) { if rb.size == 0 { return *new(T), false } return rb.items[(rb.head-1+rb.capacity)%rb.capacity], true } func (rb *RingBuffer[T]) Items() []T { if rb.size < rb.capacity { return rb.items[:rb.size] } return append(rb.items[rb.head:], rb.items[:rb.head]...) } func (rb *RingBuffer[T]) Size() int { return rb.size } func (rb *RingBuffer[T]) Capacity() int { return rb.capacity } func (rb *RingBuffer[T]) Clear() { rb.size = 0 rb.head = 0 } func (rb *RingBuffer[T]) IsFull() bool { return rb.size == rb.capacity } func (rb *RingBuffer[T]) At(i int) T { if i < 0 || i >= rb.size { panic("Index out of bounds") } if rb.size < rb.capacity { return rb.items[i] } return rb.items[(rb.head+i)%rb.capacity] } func (rb *RingBuffer[T]) Get(i int) (T, bool) { if i < 0 || i >= rb.size { return *new(T), false } if rb.size < rb.capacity { return rb.items[i], true } return rb.items[(rb.head+i)%rb.capacity], true } func (rb *RingBuffer[T]) Iter() iter.Seq[T] { return func(yield func(T) bool) { for i := 0; i < rb.size; i++ { if !yield(rb.At(i)) { return } } } } func (rb *RingBuffer[T]) Iter2() iter.Seq2[int, T] { return func(yield func(int, T) bool) { for i := 0; i < rb.size; i++ { if !yield(i, rb.At(i)) { return } } } } func (rb *RingBuffer[T]) Remove(fnEqual func(v T) bool) int { // Mike [2024-11-13]: I *really* tried to write an in-place algorithm to remove elements // But after carful consideration, I left that as an exercise for future readers // It is, suprisingly, non-trivial, especially because the head-ptr must be weirdly updated // And out At() method does not work correctly with {head<>0 && size