github.com/metacubex/mihomo@v1.18.5/transport/tuic/congestion_v2/ringbuffer.go (about)

     1  package congestion
     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 preallocs 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/quic-go/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  // Offset returns the offset 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  // and check if the index larger than buffer length.
    69  func (r *RingBuffer[T]) Offset(index int) *T {
    70  	if r.Empty() || index >= r.Len() {
    71  		panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: offset from invalid index")
    72  	}
    73  	offset := (r.headPos + index) % len(r.ring)
    74  	return &r.ring[offset]
    75  }
    76  
    77  // Front returns the front element.
    78  // It must not be called when the buffer is empty, that means that
    79  // callers might need to check if there are elements in the buffer first.
    80  func (r *RingBuffer[T]) Front() *T {
    81  	if r.Empty() {
    82  		panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: front from an empty queue")
    83  	}
    84  	return &r.ring[r.headPos]
    85  }
    86  
    87  // Back returns the back element.
    88  // It must not be called when the buffer is empty, that means that
    89  // callers might need to check if there are elements in the buffer first.
    90  func (r *RingBuffer[T]) Back() *T {
    91  	if r.Empty() {
    92  		panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: back from an empty queue")
    93  	}
    94  	return r.Offset(r.Len() - 1)
    95  }
    96  
    97  // Grow the maximum size of the queue.
    98  // This method assume the queue is full.
    99  func (r *RingBuffer[T]) grow() {
   100  	oldRing := r.ring
   101  	newSize := len(oldRing) * 2
   102  	if newSize == 0 {
   103  		newSize = 1
   104  	}
   105  	r.ring = make([]T, newSize)
   106  	headLen := copy(r.ring, oldRing[r.headPos:])
   107  	copy(r.ring[headLen:], oldRing[:r.headPos])
   108  	r.headPos, r.tailPos, r.full = 0, len(oldRing), false
   109  }
   110  
   111  // Clear removes all elements.
   112  func (r *RingBuffer[T]) Clear() {
   113  	var zeroValue T
   114  	for i := range r.ring {
   115  		r.ring[i] = zeroValue
   116  	}
   117  	r.headPos, r.tailPos, r.full = 0, 0, false
   118  }