github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/transport/hysteria/pacer.go (about) 1 package hysteria 2 3 import ( 4 "math" 5 "time" 6 7 "github.com/sagernet/quic-go/congestion" 8 ) 9 10 const ( 11 maxBurstPackets = 10 12 minPacingDelay = time.Millisecond 13 ) 14 15 // The pacer implements a token bucket pacing algorithm. 16 type pacer struct { 17 budgetAtLastSent congestion.ByteCount 18 maxDatagramSize congestion.ByteCount 19 lastSentTime time.Time 20 getBandwidth func() congestion.ByteCount // in bytes/s 21 } 22 23 func newPacer(getBandwidth func() congestion.ByteCount) *pacer { 24 p := &pacer{ 25 budgetAtLastSent: maxBurstPackets * initMaxDatagramSize, 26 maxDatagramSize: initMaxDatagramSize, 27 getBandwidth: getBandwidth, 28 } 29 return p 30 } 31 32 func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { 33 budget := p.Budget(sendTime) 34 if size > budget { 35 p.budgetAtLastSent = 0 36 } else { 37 p.budgetAtLastSent = budget - size 38 } 39 p.lastSentTime = sendTime 40 } 41 42 func (p *pacer) Budget(now time.Time) congestion.ByteCount { 43 if p.lastSentTime.IsZero() { 44 return p.maxBurstSize() 45 } 46 budget := p.budgetAtLastSent + (p.getBandwidth()*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 47 return minByteCount(p.maxBurstSize(), budget) 48 } 49 50 func (p *pacer) maxBurstSize() congestion.ByteCount { 51 return maxByteCount( 52 congestion.ByteCount((minPacingDelay+time.Millisecond).Nanoseconds())*p.getBandwidth()/1e9, 53 maxBurstPackets*p.maxDatagramSize, 54 ) 55 } 56 57 // TimeUntilSend returns when the next packet should be sent. 58 // It returns the zero value of time.Time if a packet can be sent immediately. 59 func (p *pacer) TimeUntilSend() time.Time { 60 if p.budgetAtLastSent >= p.maxDatagramSize { 61 return time.Time{} 62 } 63 return p.lastSentTime.Add(maxDuration( 64 minPacingDelay, 65 time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/ 66 float64(p.getBandwidth())))*time.Nanosecond, 67 )) 68 } 69 70 func (p *pacer) SetMaxDatagramSize(s congestion.ByteCount) { 71 p.maxDatagramSize = s 72 } 73 74 func maxByteCount(a, b congestion.ByteCount) congestion.ByteCount { 75 if a < b { 76 return b 77 } 78 return a 79 } 80 81 func minByteCount(a, b congestion.ByteCount) congestion.ByteCount { 82 if a < b { 83 return a 84 } 85 return b 86 }