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  }