func (s *sender) maybeSendSegment(seg *segment, limit int, end seqnum.Value) (sent bool) {
	if !s.isAssignedSequenceNumber(seg) {
		if seg.data.Size() != 0 {
			available := int(seg.sequenceNumber.Size(end))
			if available > limit {
				available = limit
			}
			var nextTooBig bool
			for seg.Next() != nil && seg.Next().data.Size() != 0 {
				if seg.data.Size()+seg.Next().data.Size() > available {
					nextTooBig = true
					break
				}
				seg.data.Append(seg.Next().data)
				s.writeList.Remove(seg.Next())
			}
			if !nextTooBig && seg.data.Size() < available {
				if s.outstanding > 0 && atomic.LoadUint32(&s.ep.delay) != 0 {
					return false
				}
				if atomic.LoadUint32(&s.ep.cork) != 0 {
					return false
				}
			}
		}
		seg.sequenceNumber = s.sndNxt
		seg.flags = header.TCPFlagAck | header.TCPFlagPsh
	}
	var segEnd seqnum.Value
	if seg.data.Size() == 0 {
		if s.writeList.Back() != seg {
			panic("FIN segments must be the final segment in the write list.")
		}
		seg.flags = header.TCPFlagAck | header.TCPFlagFin
		segEnd = seg.sequenceNumber.Add(1)
		s.ep.mu.Lock()
		switch s.ep.state {
		case StateCloseWait:
			s.ep.state = StateLastAck
		default:
			s.ep.state = StateFinWait1
		}
		s.ep.stack.Stats().TCP.CurrentEstablished.Decrement()
		s.ep.mu.Unlock()
	} else {
		if seg.flags&header.TCPFlagFin != 0 {
			panic("Netstack queues FIN segments without data.")
		}
		if !seg.sequenceNumber.LessThan(end) {
			return false
		}
		available := int(seg.sequenceNumber.Size(end))
		if available == 0 {
			return false
		}
		if available > limit {
			available = limit
		}
		if seg.data.Size() > available {
			s.splitSeg(seg, available)
		}
		segEnd = seg.sequenceNumber.Add(seqnum.Size(seg.data.Size()))
	}
	s.sendSegment(seg)
	if s.sndNxt.LessThan(segEnd) {
		s.sndNxt = segEnd
	}
	return true
}
maybeSendSegment tries to send the specified segment and either coalesces
other segments into this one or splits the specified segment based on the
lower of the specified limit value or the receivers window size specified by
end.
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
}
func (s *sender) pCount(seg *segment) int {
	size := seg.data.Size()
	if size == 0 {
		return 1
	}
	return (size-1)/s.maxPayloadSize + 1
}
func (s *sender) splitSeg(seg *segment, size int) {
	if seg.data.Size() <= size {
		return
	}
	nSeg := seg.clone()
	nSeg.data.TrimFront(size)
	nSeg.sequenceNumber.UpdateForward(seqnum.Size(size))
	s.writeList.InsertAfter(seg, nSeg)
	seg.data.CapLength(size)
}
func (s *sender) NextSeg() (nextSeg1, nextSeg3, nextSeg4 *segment) {
	var s3 *segment
	var s4 *segment
	smss := s.ep.scoreboard.SMSS()
	for seg := s.writeList.Front(); seg != nil; seg = seg.Next() {
		if !s.isAssignedSequenceNumber(seg) {
			break
		}
		segSeq := seg.sequenceNumber
		if seg.data.Size() > int(smss) {
			s.splitSeg(seg, int(smss))
		}
		if !s.ep.scoreboard.IsSACKED(header.SACKBlock{segSeq, segSeq.Add(1)}) {
			if s.fr.highRxt.LessThan(segSeq) && segSeq.LessThan(s.ep.scoreboard.maxSACKED) {
				if s.ep.scoreboard.IsLost(segSeq) {
					return seg, s3, s4
				}
				if s3 == nil {
					s3 = seg
				}
			}
			if s.fr.rescueRxt.LessThan(s.sndUna - 1) {
				if s4 != nil {
					if s4.sequenceNumber.LessThan(segSeq) {
						s4 = seg
					}
				} else {
					s4 = seg
				}
				s.fr.rescueRxt = s.fr.last
			}
		}
	}
	return nil, s3, s4
}