github.com/metacubex/mihomo@v1.18.5/transport/hysteria/congestion/pacer.go (about)

     1  package congestion
     2  
     3  import (
     4  	"github.com/metacubex/quic-go/congestion"
     5  	"math"
     6  	"time"
     7  )
     8  
     9  const (
    10  	maxBurstPackets = 10
    11  	minPacingDelay  = time.Millisecond
    12  )
    13  
    14  // The 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 * initMaxDatagramSize,
    25  		maxDatagramSize:  initMaxDatagramSize,
    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  	return minByteCount(p.maxBurstSize(), budget)
    47  }
    48  
    49  func (p *pacer) maxBurstSize() congestion.ByteCount {
    50  	return maxByteCount(
    51  		congestion.ByteCount((minPacingDelay+time.Millisecond).Nanoseconds())*p.getBandwidth()/1e9,
    52  		maxBurstPackets*p.maxDatagramSize,
    53  	)
    54  }
    55  
    56  // TimeUntilSend returns when the next packet should be sent.
    57  // It returns the zero value of time.Time if a packet can be sent immediately.
    58  func (p *pacer) TimeUntilSend() time.Time {
    59  	if p.budgetAtLastSent >= p.maxDatagramSize {
    60  		return time.Time{}
    61  	}
    62  	return p.lastSentTime.Add(maxDuration(
    63  		minPacingDelay,
    64  		time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/
    65  			float64(p.getBandwidth())))*time.Nanosecond,
    66  	))
    67  }
    68  
    69  func (p *pacer) SetMaxDatagramSize(s congestion.ByteCount) {
    70  	p.maxDatagramSize = s
    71  }
    72  
    73  func maxByteCount(a, b congestion.ByteCount) congestion.ByteCount {
    74  	if a < b {
    75  		return b
    76  	}
    77  	return a
    78  }
    79  
    80  func minByteCount(a, b congestion.ByteCount) congestion.ByteCount {
    81  	if a < b {
    82  		return a
    83  	}
    84  	return b
    85  }