github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/internal/utils/ringbuffer/ringbuffer.go (about)

     1  package ringbuffer
     2  
     3  // A RingBuffer is a ring buffer.
     4  // It acts as a heap that doesn't cause any allocations.
     5  type RingBuffer[T any] struct {
     6  	ring             []T
     7  	headPos, tailPos int
     8  	full             bool
     9  }
    10  
    11  // Init preallocates a buffer with a certain size.
    12  func (r *RingBuffer[T]) Init(size int) {
    13  	r.ring = make([]T, size)
    14  }
    15  
    16  // Len returns the number of elements in the ring buffer.
    17  func (r *RingBuffer[T]) Len() int {
    18  	if r.full {
    19  		return len(r.ring)
    20  	}
    21  	if r.tailPos >= r.headPos {
    22  		return r.tailPos - r.headPos
    23  	}
    24  	return r.tailPos - r.headPos + len(r.ring)
    25  }
    26  
    27  // Empty says if the ring buffer is empty.
    28  func (r *RingBuffer[T]) Empty() bool {
    29  	return !r.full && r.headPos == r.tailPos
    30  }
    31  
    32  // PushBack adds a new element.
    33  // If the ring buffer is full, its capacity is increased first.
    34  func (r *RingBuffer[T]) PushBack(t T) {
    35  	if r.full || len(r.ring) == 0 {
    36  		r.grow()
    37  	}
    38  	r.ring[r.tailPos] = t
    39  	r.tailPos++
    40  	if r.tailPos == len(r.ring) {
    41  		r.tailPos = 0
    42  	}
    43  	if r.tailPos == r.headPos {
    44  		r.full = true
    45  	}
    46  }
    47  
    48  // PopFront returns the next element.
    49  // It must not be called when the buffer is empty, that means that
    50  // callers might need to check if there are elements in the buffer first.
    51  func (r *RingBuffer[T]) PopFront() T {
    52  	if r.Empty() {
    53  		panic("github.com/daeuniverse/quic-go/internal/utils/ringbuffer: pop from an empty queue")
    54  	}
    55  	r.full = false
    56  	t := r.ring[r.headPos]
    57  	r.ring[r.headPos] = *new(T)
    58  	r.headPos++
    59  	if r.headPos == len(r.ring) {
    60  		r.headPos = 0
    61  	}
    62  	return t
    63  }
    64  
    65  // PeekFront returns the next element.
    66  // It must not be called when the buffer is empty, that means that
    67  // callers might need to check if there are elements in the buffer first.
    68  func (r *RingBuffer[T]) PeekFront() T {
    69  	if r.Empty() {
    70  		panic("github.com/daeuniverse/quic-go/internal/utils/ringbuffer: peek from an empty queue")
    71  	}
    72  	return r.ring[r.headPos]
    73  }
    74  
    75  // Grow the maximum size of the queue.
    76  // This method assume the queue is full.
    77  func (r *RingBuffer[T]) grow() {
    78  	oldRing := r.ring
    79  	newSize := len(oldRing) * 2
    80  	if newSize == 0 {
    81  		newSize = 1
    82  	}
    83  	r.ring = make([]T, newSize)
    84  	headLen := copy(r.ring, oldRing[r.headPos:])
    85  	copy(r.ring[headLen:], oldRing[:r.headPos])
    86  	r.headPos, r.tailPos, r.full = 0, len(oldRing), false
    87  }
    88  
    89  // Clear removes all elements.
    90  func (r *RingBuffer[T]) Clear() {
    91  	var zeroValue T
    92  	for i := range r.ring {
    93  		r.ring[i] = zeroValue
    94  	}
    95  	r.headPos, r.tailPos, r.full = 0, 0, false
    96  }