119 lines
3.2 KiB
Go
119 lines
3.2 KiB
Go
|
package congestion
|
||
|
|
||
|
// A RingBuffer is a ring buffer.
|
||
|
// It acts as a heap that doesn't cause any allocations.
|
||
|
type RingBuffer[T any] struct {
|
||
|
ring []T
|
||
|
headPos, tailPos int
|
||
|
full bool
|
||
|
}
|
||
|
|
||
|
// Init preallocs a buffer with a certain size.
|
||
|
func (r *RingBuffer[T]) Init(size int) {
|
||
|
r.ring = make([]T, size)
|
||
|
}
|
||
|
|
||
|
// Len returns the number of elements in the ring buffer.
|
||
|
func (r *RingBuffer[T]) Len() int {
|
||
|
if r.full {
|
||
|
return len(r.ring)
|
||
|
}
|
||
|
if r.tailPos >= r.headPos {
|
||
|
return r.tailPos - r.headPos
|
||
|
}
|
||
|
return r.tailPos - r.headPos + len(r.ring)
|
||
|
}
|
||
|
|
||
|
// Empty says if the ring buffer is empty.
|
||
|
func (r *RingBuffer[T]) Empty() bool {
|
||
|
return !r.full && r.headPos == r.tailPos
|
||
|
}
|
||
|
|
||
|
// PushBack adds a new element.
|
||
|
// If the ring buffer is full, its capacity is increased first.
|
||
|
func (r *RingBuffer[T]) PushBack(t T) {
|
||
|
if r.full || len(r.ring) == 0 {
|
||
|
r.grow()
|
||
|
}
|
||
|
r.ring[r.tailPos] = t
|
||
|
r.tailPos++
|
||
|
if r.tailPos == len(r.ring) {
|
||
|
r.tailPos = 0
|
||
|
}
|
||
|
if r.tailPos == r.headPos {
|
||
|
r.full = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// PopFront returns the next element.
|
||
|
// It must not be called when the buffer is empty, that means that
|
||
|
// callers might need to check if there are elements in the buffer first.
|
||
|
func (r *RingBuffer[T]) PopFront() T {
|
||
|
if r.Empty() {
|
||
|
panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: pop from an empty queue")
|
||
|
}
|
||
|
r.full = false
|
||
|
t := r.ring[r.headPos]
|
||
|
r.ring[r.headPos] = *new(T)
|
||
|
r.headPos++
|
||
|
if r.headPos == len(r.ring) {
|
||
|
r.headPos = 0
|
||
|
}
|
||
|
return t
|
||
|
}
|
||
|
|
||
|
// Offset returns the offset element.
|
||
|
// It must not be called when the buffer is empty, that means that
|
||
|
// callers might need to check if there are elements in the buffer first
|
||
|
// and check if the index larger than buffer length.
|
||
|
func (r *RingBuffer[T]) Offset(index int) *T {
|
||
|
if r.Empty() || index >= r.Len() {
|
||
|
panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: offset from invalid index")
|
||
|
}
|
||
|
offset := (r.headPos + index) % len(r.ring)
|
||
|
return &r.ring[offset]
|
||
|
}
|
||
|
|
||
|
// Front returns the front element.
|
||
|
// It must not be called when the buffer is empty, that means that
|
||
|
// callers might need to check if there are elements in the buffer first.
|
||
|
func (r *RingBuffer[T]) Front() *T {
|
||
|
if r.Empty() {
|
||
|
panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: front from an empty queue")
|
||
|
}
|
||
|
return &r.ring[r.headPos]
|
||
|
}
|
||
|
|
||
|
// Back returns the back element.
|
||
|
// It must not be called when the buffer is empty, that means that
|
||
|
// callers might need to check if there are elements in the buffer first.
|
||
|
func (r *RingBuffer[T]) Back() *T {
|
||
|
if r.Empty() {
|
||
|
panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: back from an empty queue")
|
||
|
}
|
||
|
return r.Offset(r.Len() - 1)
|
||
|
}
|
||
|
|
||
|
// Grow the maximum size of the queue.
|
||
|
// This method assume the queue is full.
|
||
|
func (r *RingBuffer[T]) grow() {
|
||
|
oldRing := r.ring
|
||
|
newSize := len(oldRing) * 2
|
||
|
if newSize == 0 {
|
||
|
newSize = 1
|
||
|
}
|
||
|
r.ring = make([]T, newSize)
|
||
|
headLen := copy(r.ring, oldRing[r.headPos:])
|
||
|
copy(r.ring[headLen:], oldRing[:r.headPos])
|
||
|
r.headPos, r.tailPos, r.full = 0, len(oldRing), false
|
||
|
}
|
||
|
|
||
|
// Clear removes all elements.
|
||
|
func (r *RingBuffer[T]) Clear() {
|
||
|
var zeroValue T
|
||
|
for i := range r.ring {
|
||
|
r.ring[i] = zeroValue
|
||
|
}
|
||
|
r.headPos, r.tailPos, r.full = 0, 0, false
|
||
|
}
|