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  }