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 }