github.com/metacubex/mihomo@v1.18.5/transport/tuic/congestion_v2/packet_number_indexed_queue.go (about) 1 package congestion 2 3 import ( 4 "github.com/metacubex/quic-go/congestion" 5 ) 6 7 // packetNumberIndexedQueue is a queue of mostly continuous numbered entries 8 // which supports the following operations: 9 // - adding elements to the end of the queue, or at some point past the end 10 // - removing elements in any order 11 // - retrieving elements 12 // If all elements are inserted in order, all of the operations above are 13 // amortized O(1) time. 14 // 15 // Internally, the data structure is a deque where each element is marked as 16 // present or not. The deque starts at the lowest present index. Whenever an 17 // element is removed, it's marked as not present, and the front of the deque is 18 // cleared of elements that are not present. 19 // 20 // The tail of the queue is not cleared due to the assumption of entries being 21 // inserted in order, though removing all elements of the queue will return it 22 // to its initial state. 23 // 24 // Note that this data structure is inherently hazardous, since an addition of 25 // just two entries will cause it to consume all of the memory available. 26 // Because of that, it is not a general-purpose container and should not be used 27 // as one. 28 29 type entryWrapper[T any] struct { 30 present bool 31 entry T 32 } 33 34 type packetNumberIndexedQueue[T any] struct { 35 entries RingBuffer[entryWrapper[T]] 36 numberOfPresentEntries int 37 firstPacket congestion.PacketNumber 38 } 39 40 func newPacketNumberIndexedQueue[T any](size int) *packetNumberIndexedQueue[T] { 41 q := &packetNumberIndexedQueue[T]{ 42 firstPacket: invalidPacketNumber, 43 } 44 45 q.entries.Init(size) 46 47 return q 48 } 49 50 // Emplace inserts data associated |packet_number| into (or past) the end of the 51 // queue, filling up the missing intermediate entries as necessary. Returns 52 // true if the element has been inserted successfully, false if it was already 53 // in the queue or inserted out of order. 54 func (p *packetNumberIndexedQueue[T]) Emplace(packetNumber congestion.PacketNumber, entry *T) bool { 55 if packetNumber == invalidPacketNumber || entry == nil { 56 return false 57 } 58 59 if p.IsEmpty() { 60 p.entries.PushBack(entryWrapper[T]{ 61 present: true, 62 entry: *entry, 63 }) 64 p.numberOfPresentEntries = 1 65 p.firstPacket = packetNumber 66 return true 67 } 68 69 // Do not allow insertion out-of-order. 70 if packetNumber <= p.LastPacket() { 71 return false 72 } 73 74 // Handle potentially missing elements. 75 offset := int(packetNumber - p.FirstPacket()) 76 if gap := offset - p.entries.Len(); gap > 0 { 77 for i := 0; i < gap; i++ { 78 p.entries.PushBack(entryWrapper[T]{}) 79 } 80 } 81 82 p.entries.PushBack(entryWrapper[T]{ 83 present: true, 84 entry: *entry, 85 }) 86 p.numberOfPresentEntries++ 87 return true 88 } 89 90 // GetEntry Retrieve the entry associated with the packet number. Returns the pointer 91 // to the entry in case of success, or nullptr if the entry does not exist. 92 func (p *packetNumberIndexedQueue[T]) GetEntry(packetNumber congestion.PacketNumber) *T { 93 ew := p.getEntryWraper(packetNumber) 94 if ew == nil { 95 return nil 96 } 97 98 return &ew.entry 99 } 100 101 // Remove, Same as above, but if an entry is present in the queue, also call f(entry) 102 // before removing it. 103 func (p *packetNumberIndexedQueue[T]) Remove(packetNumber congestion.PacketNumber, f func(T)) bool { 104 ew := p.getEntryWraper(packetNumber) 105 if ew == nil { 106 return false 107 } 108 if f != nil { 109 f(ew.entry) 110 } 111 ew.present = false 112 p.numberOfPresentEntries-- 113 114 if packetNumber == p.FirstPacket() { 115 p.clearup() 116 } 117 118 return true 119 } 120 121 // RemoveUpTo, but not including |packet_number|. 122 // Unused slots in the front are also removed, which means when the function 123 // returns, |first_packet()| can be larger than |packet_number|. 124 func (p *packetNumberIndexedQueue[T]) RemoveUpTo(packetNumber congestion.PacketNumber) { 125 for !p.entries.Empty() && 126 p.firstPacket != invalidPacketNumber && 127 p.firstPacket < packetNumber { 128 if p.entries.Front().present { 129 p.numberOfPresentEntries-- 130 } 131 p.entries.PopFront() 132 p.firstPacket++ 133 } 134 p.clearup() 135 136 return 137 } 138 139 // IsEmpty return if queue is empty. 140 func (p *packetNumberIndexedQueue[T]) IsEmpty() bool { 141 return p.numberOfPresentEntries == 0 142 } 143 144 // NumberOfPresentEntries returns the number of entries in the queue. 145 func (p *packetNumberIndexedQueue[T]) NumberOfPresentEntries() int { 146 return p.numberOfPresentEntries 147 } 148 149 // EntrySlotsUsed returns the number of entries allocated in the underlying deque. This is 150 // proportional to the memory usage of the queue. 151 func (p *packetNumberIndexedQueue[T]) EntrySlotsUsed() int { 152 return p.entries.Len() 153 } 154 155 // LastPacket returns packet number of the first entry in the queue. 156 func (p *packetNumberIndexedQueue[T]) FirstPacket() (packetNumber congestion.PacketNumber) { 157 return p.firstPacket 158 } 159 160 // LastPacket returns packet number of the last entry ever inserted in the queue. Note that the 161 // entry in question may have already been removed. Zero if the queue is 162 // empty. 163 func (p *packetNumberIndexedQueue[T]) LastPacket() (packetNumber congestion.PacketNumber) { 164 if p.IsEmpty() { 165 return invalidPacketNumber 166 } 167 168 return p.firstPacket + congestion.PacketNumber(p.entries.Len()-1) 169 } 170 171 func (p *packetNumberIndexedQueue[T]) clearup() { 172 for !p.entries.Empty() && !p.entries.Front().present { 173 p.entries.PopFront() 174 p.firstPacket++ 175 } 176 if p.entries.Empty() { 177 p.firstPacket = invalidPacketNumber 178 } 179 } 180 181 func (p *packetNumberIndexedQueue[T]) getEntryWraper(packetNumber congestion.PacketNumber) *entryWrapper[T] { 182 if packetNumber == invalidPacketNumber || 183 p.IsEmpty() || 184 packetNumber < p.firstPacket { 185 return nil 186 } 187 188 offset := int(packetNumber - p.firstPacket) 189 if offset >= p.entries.Len() { 190 return nil 191 } 192 193 ew := p.entries.Offset(offset) 194 if ew == nil || !ew.present { 195 return nil 196 } 197 198 return ew 199 }