2024-10-05 01:28:46 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-11-13 15:03:51 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-05 01:28:46 +02:00
|
|
|
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")
|
|
|
|
}
|
2024-10-05 01:41:10 +02:00
|
|
|
if rb.size < rb.capacity {
|
|
|
|
return rb.items[i]
|
|
|
|
}
|
2024-10-05 01:28:46 +02:00
|
|
|
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
|
|
|
|
}
|
2024-10-05 01:41:10 +02:00
|
|
|
if rb.size < rb.capacity {
|
|
|
|
return rb.items[i], true
|
|
|
|
}
|
2024-10-05 01:28:46 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-13 15:03:51 +01:00
|
|
|
|
|
|
|
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<capacity}
|
|
|
|
|
|
|
|
dc := 0
|
|
|
|
b := make([]T, rb.capacity)
|
|
|
|
bsize := 0
|
|
|
|
|
|
|
|
for i := 0; i < rb.size; i++ {
|
|
|
|
comp := rb.At(i)
|
|
|
|
if fnEqual(comp) {
|
|
|
|
dc++
|
|
|
|
} else {
|
|
|
|
b[bsize] = comp
|
|
|
|
bsize++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if dc == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
rb.items = b
|
|
|
|
rb.size = bsize
|
|
|
|
rb.head = bsize % rb.capacity
|
|
|
|
|
|
|
|
return dc
|
|
|
|
|
|
|
|
}
|