func (s *sender) pCount(seg *segment) int {
	size := seg.data.Size()
	if size == 0 {
		return 1
	}
	return (size-1)/s.maxPayloadSize + 1
}
pCount returns the number of packets in the segment. Due to GSO, a segment
can be composed of multiple packets.
package tcp
import (
	"math"
	"sync"
	"sync/atomic"
	"time"
	"github.com/google/netstack/sleep"
	"github.com/google/netstack/tcpip"
	"github.com/google/netstack/tcpip/buffer"
	"github.com/google/netstack/tcpip/header"
	"github.com/google/netstack/tcpip/seqnum"
)
const (
	minRTO = 200 * time.Millisecond
	InitialCwnd = 10
	nDupAckThreshold = 3
)
type ccState int
const (
	Open ccState = iota
	RTORecovery
	FastRecovery
	SACKRecovery
	Disorder
)
type congestionControl interface {
	HandleNDupAcks()
	HandleRTOExpired()
	Update(packetsAcked int)
	PostRecovery()
}
type sender struct {
	ep *endpoint
	lastSendTime time.Time
	dupAckCount int
	fr fastRecovery
	sndCwnd int
	sndSsthresh int
	sndCAAckCount int
	outstanding int
	sndWnd seqnum.Size
	sndUna seqnum.Value
	sndNxt seqnum.Value
	sndNxtList seqnum.Value
	rttMeasureSeqNum seqnum.Value
	rttMeasureTime time.Time
	closed      bool
	writeNext   *segment
	writeList   segmentList
	resendTimer timer
	resendWaker sleep.Waker
	rtt rtt
	rto time.Duration
	maxPayloadSize int
	gso bool
	sndWndScale uint8
	maxSentAck seqnum.Value
	state ccState
	cc congestionControl
}
type rtt struct {
	sync.Mutex
	srtt       time.Duration
	rttvar     time.Duration
	srttInited bool
}
type fastRecovery struct {
	active bool
	first seqnum.Value
	last  seqnum.Value
	maxCwnd int
	highRxt seqnum.Value
	rescueRxt seqnum.Value
}
func newSender(ep *endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint16, sndWndScale int) *sender {
	maxPayloadSize := int(mss) - ep.maxOptionSize()
	s := &sender{
		ep:               ep,
		sndWnd:           sndWnd,
		sndUna:           iss + 1,
		sndNxt:           iss + 1,
		sndNxtList:       iss + 1,
		rto:              1 * time.Second,
		rttMeasureSeqNum: iss + 1,
		lastSendTime:     time.Now(),
		maxPayloadSize:   maxPayloadSize,
		maxSentAck:       irs + 1,
		fr: fastRecovery{
			last:      iss,
			highRxt:   iss,
			rescueRxt: iss,
		},
		gso: ep.gso != nil,
	}
	if s.gso {
		s.ep.gso.MSS = uint16(maxPayloadSize)
	}
	s.cc = s.initCongestionControl(ep.cc)
	if sndWndScale > 0 {
		s.sndWndScale = uint8(sndWndScale)
	}
	s.resendTimer.init(&s.resendWaker)
	s.updateMaxPayloadSize(int(ep.route.MTU()), 0)
	s.ep.scoreboard = NewSACKScoreboard(uint16(s.maxPayloadSize), iss)
	return s
}
func (s *sender) initCongestionControl(congestionControlName tcpip.CongestionControlOption) congestionControl {
	s.sndCwnd = InitialCwnd
	s.sndSsthresh = math.MaxInt64
	switch congestionControlName {
	case ccCubic:
		return newCubicCC(s)
	case ccReno:
		fallthrough
	default:
		return newRenoCC(s)
	}
}
func (s *sender) updateMaxPayloadSize(mtu, count int) {
	m := mtu - header.TCPMinimumSize
	m -= s.ep.maxOptionSize()
	if m >= s.maxPayloadSize {
		return
	}
	if m <= 0 {
		m = 1
	}
	s.maxPayloadSize = m
	if s.gso {
		s.ep.gso.MSS = uint16(m)
	}
	if count == 0 {
		return
	}
	s.ep.scoreboard.smss = uint16(m)
	s.outstanding -= count
	if s.outstanding < 0 {
		s.outstanding = 0
	}
	for seg := s.writeList.Front(); seg != nil; seg = seg.Next() {
		if seg == s.writeNext {
			break
		}
		if seg.data.Size() > m {
			s.writeNext = seg
			break
		}
	}
	s.sendData()
}
func (s *sender) sendAck() {
	s.sendSegmentFromView(buffer.VectorisedView{}, header.TCPFlagAck, s.sndNxt)
}
func (s *sender) updateRTO(rtt time.Duration) {
	s.rtt.Lock()
	if !s.rtt.srttInited {
		s.rtt.rttvar = rtt / 2
		s.rtt.srtt = rtt
		s.rtt.srttInited = true
	} else {
		diff := s.rtt.srtt - rtt
		if diff < 0 {
			diff = -diff
		}
		if !s.ep.sendTSOk {
			s.rtt.rttvar = (3*s.rtt.rttvar + diff) / 4
			s.rtt.srtt = (7*s.rtt.srtt + rtt) / 8
		} else {
			if s.outstanding == 0 {
				s.rtt.Unlock()
				return
			}
			expectedSamples := math.Ceil(float64(s.outstanding) / 2)
			const alpha = 0.125
			const beta = 0.25
			alphaPrime := alpha / expectedSamples
			betaPrime := beta / expectedSamples
			rttVar := (1-betaPrime)*s.rtt.rttvar.Seconds() + betaPrime*diff.Seconds()
			srtt := (1-alphaPrime)*s.rtt.srtt.Seconds() + alphaPrime*rtt.Seconds()
			s.rtt.rttvar = time.Duration(rttVar * float64(time.Second))
			s.rtt.srtt = time.Duration(srtt * float64(time.Second))
		}
	}
	s.rto = s.rtt.srtt + 4*s.rtt.rttvar
	s.rtt.Unlock()
	if s.rto < minRTO {
		s.rto = minRTO
	}
}
func (s *sender) resendSegment() {
	s.rttMeasureSeqNum = s.sndNxt
	if seg := s.writeList.Front(); seg != nil {
		if seg.data.Size() > s.maxPayloadSize {
			s.splitSeg(seg, s.maxPayloadSize)
		}
		s.fr.highRxt = seg.sequenceNumber.Add(seqnum.Size(seg.data.Size())) - 1
		s.fr.rescueRxt = seg.sequenceNumber.Add(seqnum.Size(seg.data.Size())) - 1
		s.sendSegment(seg)
		s.ep.stack.Stats().TCP.FastRetransmit.Increment()
		s.ep.stats.SendErrors.FastRetransmit.Increment()
		s.SetPipe()
	}
}
func (s *sender) retransmitTimerExpired() bool {
	if !s.resendTimer.checkExpiration() {
		return true
	}
	s.ep.stack.Stats().TCP.Timeouts.Increment()
	s.ep.stats.SendErrors.Timeouts.Increment()
	if s.rto >= 60*time.Second {
		return false
	}
	s.rto *= 2
	s.fr.last = s.sndNxt - 1
	if s.fr.active {
		s.leaveFastRecovery()
	}
	s.state = RTORecovery
	s.cc.HandleRTOExpired()
	s.outstanding = 0
	s.ep.scoreboard.Reset()
	s.writeNext = s.writeList.Front()
	s.sendData()
	return true
}