github.com/metacubex/mihomo@v1.18.5/transport/tuic/congestion_v2/pacer.go (about) 1 package congestion 2 3 import ( 4 "math" 5 "time" 6 7 "github.com/metacubex/quic-go/congestion" 8 ) 9 10 const ( 11 maxBurstPackets = 10 12 ) 13 14 // Pacer implements a token bucket pacing algorithm. 15 type Pacer struct { 16 budgetAtLastSent congestion.ByteCount 17 maxDatagramSize congestion.ByteCount 18 lastSentTime time.Time 19 getBandwidth func() congestion.ByteCount // in bytes/s 20 } 21 22 func NewPacer(getBandwidth func() congestion.ByteCount) *Pacer { 23 p := &Pacer{ 24 budgetAtLastSent: maxBurstPackets * congestion.InitialPacketSizeIPv4, 25 maxDatagramSize: congestion.InitialPacketSizeIPv4, 26 getBandwidth: getBandwidth, 27 } 28 return p 29 } 30 31 func (p *Pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { 32 budget := p.Budget(sendTime) 33 if size > budget { 34 p.budgetAtLastSent = 0 35 } else { 36 p.budgetAtLastSent = budget - size 37 } 38 p.lastSentTime = sendTime 39 } 40 41 func (p *Pacer) Budget(now time.Time) congestion.ByteCount { 42 if p.lastSentTime.IsZero() { 43 return p.maxBurstSize() 44 } 45 budget := p.budgetAtLastSent + (p.getBandwidth()*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 46 if budget < 0 { // protect against overflows 47 budget = congestion.ByteCount(1<<62 - 1) 48 } 49 return Min(p.maxBurstSize(), budget) 50 } 51 52 func (p *Pacer) maxBurstSize() congestion.ByteCount { 53 return Max( 54 congestion.ByteCount((congestion.MinPacingDelay+time.Millisecond).Nanoseconds())*p.getBandwidth()/1e9, 55 maxBurstPackets*p.maxDatagramSize, 56 ) 57 } 58 59 // TimeUntilSend returns when the next packet should be sent. 60 // It returns the zero value of time.Time if a packet can be sent immediately. 61 func (p *Pacer) TimeUntilSend() time.Time { 62 if p.budgetAtLastSent >= p.maxDatagramSize { 63 return time.Time{} 64 } 65 return p.lastSentTime.Add(Max( 66 congestion.MinPacingDelay, 67 time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/ 68 float64(p.getBandwidth())))*time.Nanosecond, 69 )) 70 } 71 72 func (p *Pacer) SetMaxDatagramSize(s congestion.ByteCount) { 73 p.maxDatagramSize = s 74 }