func (s *tlsExtension) writeVarint(v uint64) {
	n := varintLen(v)
	*s = appendVarint(*s, v, n)
}
writeVarint appends integer.
package transport
import (
	"crypto/tls"
	"fmt"
	"time"
	"github.com/goburrow/quic/tls13"
)
const (
	paramOriginalDestinationCID         = 0x00
	paramMaxIdleTimeout                 = 0x01
	paramStatelessResetToken            = 0x02
	paramMaxUDPPayloadSize              = 0x03
	paramInitialMaxData                 = 0x04
	paramInitialMaxStreamDataBidiLocal  = 0x05
	paramInitialMaxStreamDataBidiRemote = 0x06
	paramInitialMaxStreamDataUni        = 0x07
	paramInitialMaxStreamsBidi          = 0x08
	paramInitialMaxStreamsUni           = 0x09
	paramAckDelayExponent               = 0x0a
	paramMaxAckDelay                    = 0x0b
	paramDisableActiveMigration         = 0x0c
	paramActiveConnectionIDLimit        = 0x0e
	paramInitialSourceCID               = 0x0f
	paramRetrySourceCID                 = 0x10
	paramMaxDatagramPayloadSize = 0x20
)
const (
	defaultMaxUDPPayloadSize = 65527
	defaultAckDelayExponent = 3
	defaultMaxAckDelay = 25 * time.Millisecond
)
type Parameters struct {
	OriginalDestinationCID []byte
	InitialSourceCID []byte
	RetrySourceCID []byte
	StatelessResetToken []byte
	MaxIdleTimeout time.Duration
	MaxUDPPayloadSize uint64
	InitialMaxData                 uint64
	InitialMaxStreamDataBidiLocal  uint64
	InitialMaxStreamDataBidiRemote uint64
	InitialMaxStreamDataUni        uint64
	InitialMaxStreamsBidi          uint64
	InitialMaxStreamsUni           uint64
	AckDelayExponent uint64
	MaxAckDelay time.Duration
	ActiveConnectionIDLimit uint64
	MaxDatagramFramePayloadSize uint64
	DisableActiveMigration bool
}
func (s *Parameters) marshal() []byte {
	b := make(tlsExtension, 0, 128)
	if len(s.OriginalDestinationCID) > 0 {
		b.writeVarint(paramOriginalDestinationCID)
		b.writeBytes(s.OriginalDestinationCID)
	}
	if s.MaxIdleTimeout > 0 {
		b.writeVarint(paramMaxIdleTimeout)
		b.writeUint(uint64(s.MaxIdleTimeout / time.Millisecond))
	}
	if len(s.StatelessResetToken) > 0 {
		b.writeVarint(paramStatelessResetToken)
		b.writeBytes(s.StatelessResetToken)
	}
	if s.MaxUDPPayloadSize > 0 {
		b.writeVarint(paramMaxUDPPayloadSize)
		b.writeUint(s.MaxUDPPayloadSize)
	}
	if s.InitialMaxData > 0 {
		b.writeVarint(paramInitialMaxData)
		b.writeUint(s.InitialMaxData)
	}
	if s.InitialMaxStreamDataBidiLocal > 0 {
		b.writeVarint(paramInitialMaxStreamDataBidiLocal)
		b.writeUint(s.InitialMaxStreamDataBidiLocal)
	}
	if s.InitialMaxStreamDataBidiRemote > 0 {
		b.writeVarint(paramInitialMaxStreamDataBidiRemote)
		b.writeUint(s.InitialMaxStreamDataBidiRemote)
	}
	if s.InitialMaxStreamDataUni > 0 {
		b.writeVarint(paramInitialMaxStreamDataUni)
		b.writeUint(s.InitialMaxStreamDataUni)
	}
	if s.InitialMaxStreamsBidi > 0 {
		b.writeVarint(paramInitialMaxStreamsBidi)
		b.writeUint(s.InitialMaxStreamsBidi)
	}
	if s.InitialMaxStreamsUni > 0 {
		b.writeVarint(paramInitialMaxStreamsUni)
		b.writeUint(s.InitialMaxStreamsUni)
	}
	if s.AckDelayExponent > 0 {
		b.writeVarint(paramAckDelayExponent)
		b.writeUint(s.AckDelayExponent)
	}
	if s.MaxAckDelay > 0 {
		b.writeVarint(paramMaxAckDelay)
		b.writeUint(uint64(s.MaxAckDelay / time.Millisecond))
	}
	if s.DisableActiveMigration {
		b.writeVarint(paramDisableActiveMigration)
		b.writeVarint(0)
	}
	if s.ActiveConnectionIDLimit > 0 {
		b.writeVarint(paramActiveConnectionIDLimit)
		b.writeUint(s.ActiveConnectionIDLimit)
	}
	if len(s.InitialSourceCID) > 0 {
		b.writeVarint(paramInitialSourceCID)
		b.writeBytes(s.InitialSourceCID)
	}
	if len(s.RetrySourceCID) > 0 {
		b.writeVarint(paramRetrySourceCID)
		b.writeBytes(s.RetrySourceCID)
	}
	if s.MaxDatagramFramePayloadSize > 0 {
		b.writeVarint(paramMaxDatagramPayloadSize)
		b.writeUint(s.MaxDatagramFramePayloadSize)
	}
	return b
}
func (s *Parameters) unmarshal(data []byte) bool {
	b := tlsExtension(data)
	var param uint64
	for !b.empty() {
		if !b.readVarint(&param) {
			return false
		}
		switch param {
		case paramOriginalDestinationCID:
			if !b.readBytes(&s.OriginalDestinationCID) {
				return false
			}
		case paramMaxIdleTimeout:
			if !b.readUint(&param) {
				return false
			}
			s.MaxIdleTimeout = time.Duration(param) * time.Millisecond
		case paramStatelessResetToken:
			if !b.readBytes(&s.StatelessResetToken) {
				return false
			}
		case paramMaxUDPPayloadSize:
			if !b.readUint(&s.MaxUDPPayloadSize) {
				return false
			}
		case paramInitialMaxData:
			if !b.readUint(&s.InitialMaxData) {
				return false
			}
		case paramInitialMaxStreamDataBidiLocal:
			if !b.readUint(&s.InitialMaxStreamDataBidiLocal) {
				return false
			}
		case paramInitialMaxStreamDataBidiRemote:
			if !b.readUint(&s.InitialMaxStreamDataBidiRemote) {
				return false
			}
		case paramInitialMaxStreamDataUni:
			if !b.readUint(&s.InitialMaxStreamDataUni) {
				return false
			}
		case paramInitialMaxStreamsBidi:
			if !b.readUint(&s.InitialMaxStreamsBidi) {
				return false
			}
		case paramInitialMaxStreamsUni:
			if !b.readUint(&s.InitialMaxStreamsUni) {
				return false
			}
		case paramAckDelayExponent:
			if !b.readUint(&s.AckDelayExponent) {
				return false
			}
		case paramMaxAckDelay:
			if !b.readUint(&param) {
				return false
			}
			s.MaxAckDelay = time.Duration(param) * time.Millisecond
		case paramDisableActiveMigration:
			if !b.readVarint(&param) || param != 0 {
				return false
			}
			s.DisableActiveMigration = true
		case paramActiveConnectionIDLimit:
			if !b.readUint(&s.ActiveConnectionIDLimit) {
				return false
			}
		case paramInitialSourceCID:
			if !b.readBytes(&s.InitialSourceCID) {
				return false
			}
		case paramRetrySourceCID:
			if !b.readBytes(&s.RetrySourceCID) {
				return false
			}
		case paramMaxDatagramPayloadSize:
			if !b.readUint(&s.MaxDatagramFramePayloadSize) {
				return false
			}
		default:
			debug("skip unsupported transport parameter 0x%x", param)
			if !(b.readVarint(&param) && b.skip(int(param))) {
				return false
			}
		}
	}
	return true
}
func (s *Parameters) validate(isClient bool) error {
	if len(s.OriginalDestinationCID) != 0 && isClient {
		return newError(TransportParameterError, "original_destination_connection_id")
	}
	if len(s.StatelessResetToken) != 0 {
		if isClient || len(s.StatelessResetToken) != 16 {
			return newError(TransportParameterError, "stateless_reset_token")
		}
	}
	if s.MaxUDPPayloadSize > 0 && s.MaxUDPPayloadSize < MinInitialPacketSize {
		return newError(TransportParameterError, "max_udp_payload_size")
	}
	if s.AckDelayExponent > 20 {
		return newError(TransportParameterError, "ack_delay_exponent")
	}
	if s.MaxAckDelay < 0 || s.MaxAckDelay >= 1<<14*time.Millisecond {
		return newError(TransportParameterError, "max_ack_delay")
	}
	if s.InitialMaxStreamsBidi > maxStreams {
		return newError(StreamLimitError, "initial_max_streams_bidi")
	}
	if s.InitialMaxStreamsUni > maxStreams {
		return newError(StreamLimitError, "initial_max_streams_uni")
	}
	return nil
}
func (s *Parameters) log(b []byte) []byte {
	if len(s.OriginalDestinationCID) > 0 {
		b = appendField(b, "original_connection_id", s.OriginalDestinationCID)
	}
	if s.MaxIdleTimeout > 0 {
		b = appendField(b, "max_idle_timeout", s.MaxIdleTimeout)
	}
	if len(s.StatelessResetToken) > 0 {
		b = appendField(b, "stateless_reset_token", s.StatelessResetToken)
	}
	if s.MaxUDPPayloadSize > 0 {
		b = appendField(b, "max_udp_payload_size", s.MaxUDPPayloadSize)
	}
	if s.InitialMaxData > 0 {
		b = appendField(b, "initial_max_data", s.InitialMaxData)
	}
	if s.InitialMaxStreamDataBidiLocal > 0 {
		b = appendField(b, "initial_max_stream_data_bidi_local", s.InitialMaxStreamDataBidiLocal)
	}
	if s.InitialMaxStreamDataBidiRemote > 0 {
		b = appendField(b, "initial_max_stream_data_bidi_remote", s.InitialMaxStreamDataBidiRemote)
	}
	if s.InitialMaxStreamDataUni > 0 {
		b = appendField(b, "initial_max_stream_data_uni", s.InitialMaxStreamDataUni)
	}
	if s.InitialMaxStreamsBidi > 0 {
		b = appendField(b, "initial_max_streams_bidi", s.InitialMaxStreamsBidi)
	}
	if s.InitialMaxStreamsUni > 0 {
		b = appendField(b, "initial_max_streams_uni", s.InitialMaxStreamsUni)
	}
	if s.AckDelayExponent > 0 {
		b = appendField(b, "ack_delay_exponent", s.AckDelayExponent)
	}
	if s.MaxAckDelay > 0 {
		b = appendField(b, "max_ack_delay", s.MaxAckDelay)
	}
	if s.DisableActiveMigration {
		b = appendField(b, "disable_active_migration", s.DisableActiveMigration)
	}
	if s.ActiveConnectionIDLimit > 0 {
		b = appendField(b, "active_connection_id_limit", s.ActiveConnectionIDLimit)
	}
	if len(s.InitialSourceCID) > 0 {
		b = appendField(b, "initial_source_connection_id", s.InitialSourceCID)
	}
	if len(s.RetrySourceCID) > 0 {
		b = appendField(b, "retry_source_connection_id", s.RetrySourceCID)
	}
	return b
}
func (s *Parameters) String() string {
	return fmt.Sprintf("original_destination_connection_id=%x initial_source_connection_id=%x retry_source_connection_id=%x stateless_reset_token=%x "+
		"max_udp_payload_size=%v max_datagram_frame_payload_size=%v disable_active_migration=%v active_connection_id_limit=%v "+
		"max_idle_timeout=%v max_ack_delay=%v ack_delay_exponent=%v "+
		"initial_max_data=%v initial_max_stream_data_bidi_local=%v initial_max_stream_data_bidi_remote=%v initial_max_stream_data_uni=%v "+
		"initial_max_streams_bidi=%v initial_max_streams_uni=%v",
		s.OriginalDestinationCID, s.InitialSourceCID, s.RetrySourceCID, s.StatelessResetToken,
		s.MaxUDPPayloadSize, s.MaxDatagramFramePayloadSize, s.DisableActiveMigration, s.ActiveConnectionIDLimit,
		s.MaxIdleTimeout, s.MaxAckDelay, s.AckDelayExponent,
		s.InitialMaxData, s.InitialMaxStreamDataBidiLocal, s.InitialMaxStreamDataBidiRemote, s.InitialMaxStreamDataUni,
		s.InitialMaxStreamsBidi, s.InitialMaxStreamsUni)
}
type tlsExtension []byte
func (s *tlsExtension) readVarint(v *uint64) bool {
	b := *s
	if len(b) == 0 {
		return false
	}
	n := getVarint(b, v)
	if n <= 0 {
		return false
	}
	*s = b[n:]
	return true
}
func (s *tlsExtension) readUint(v *uint64) bool {
	var n uint64
	if !s.readVarint(&n) {
		return false
	}
	if n > 0 {
		b := *s
		m := getVarint(b, v)
		if m <= 0 || uint64(m) != n {
			return false
		}
		*s = b[n:]
	}
	return true
}
func (s *tlsExtension) readBytes(v *[]byte) bool {
	var n uint64
	if !s.readVarint(&n) {
		return false
	}
	if n > 0 {
		b := *s
		if len(b) < int(n) {
			return false
		}
		*v = append((*v)[:0], b[:n]...)
		*s = b[n:]
	}
	return true
}