// Copyright (c) Faye Amacker. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

package cbor

import (
	"bytes"
	"encoding"
	"encoding/binary"
	"errors"
	"io"
	"math"
	"math/big"
	"reflect"
	"sort"
	"strconv"
	"sync"
	"time"

	"github.com/x448/float16"
)

// Marshal returns the CBOR encoding of v using default encoding options.
// See EncOptions for encoding options.
//
// Marshal uses the following encoding rules:
//
// If value implements the Marshaler interface, Marshal calls its
// MarshalCBOR method.
//
// If value implements encoding.BinaryMarshaler, Marhsal calls its
// MarshalBinary method and encode it as CBOR byte string.
//
// Boolean values encode as CBOR booleans (type 7).
//
// Positive integer values encode as CBOR positive integers (type 0).
//
// Negative integer values encode as CBOR negative integers (type 1).
//
// Floating point values encode as CBOR floating points (type 7).
//
// String values encode as CBOR text strings (type 3).
//
// []byte values encode as CBOR byte strings (type 2).
//
// Array and slice values encode as CBOR arrays (type 4).
//
// Map values encode as CBOR maps (type 5).
//
// Struct values encode as CBOR maps (type 5).  Each exported struct field
// becomes a pair with field name encoded as CBOR text string (type 3) and
// field value encoded based on its type.  See struct tag option "keyasint"
// to encode field name as CBOR integer (type 0 and 1).  Also see struct
// tag option "toarray" for special field "_" to encode struct values as
// CBOR array (type 4).
//
// Marshal supports format string stored under the "cbor" key in the struct
// field's tag.  CBOR format string can specify the name of the field,
// "omitempty" and "keyasint" options, and special case "-" for field omission.
// If "cbor" key is absent, Marshal uses "json" key.
//
// Struct field name is treated as integer if it has "keyasint" option in
// its format string.  The format string must specify an integer as its
// field name.
//
// Special struct field "_" is used to specify struct level options, such as
// "toarray". "toarray" option enables Go struct to be encoded as CBOR array.
// "omitempty" is disabled by "toarray" to ensure that the same number
// of elements are encoded every time.
//
// Anonymous struct fields are marshaled as if their exported fields
// were fields in the outer struct.  Marshal follows the same struct fields
// visibility rules used by JSON encoding package.
//
// time.Time values encode as text strings specified in RFC3339 or numerical
// representation of seconds since January 1, 1970 UTC depending on
// EncOptions.Time setting.  Also See EncOptions.TimeTag to encode
// time.Time as CBOR tag with tag number 0 or 1.
//
// big.Int values encode as CBOR integers (type 0 and 1) if values fit.
// Otherwise, big.Int values encode as CBOR bignums (tag 2 and 3).  See
// EncOptions.BigIntConvert to always encode big.Int values as CBOR
// bignums.
//
// Pointer values encode as the value pointed to.
//
// Interface values encode as the value stored in the interface.
//
// Nil slice/map/pointer/interface values encode as CBOR nulls (type 7).
//
// Values of other types cannot be encoded in CBOR.  Attempting
// to encode such a value causes Marshal to return an UnsupportedTypeError.
func Marshal(v interface{}) ([]byte, error) {
	return defaultEncMode.Marshal(v)
}

// Marshaler is the interface implemented by types that can marshal themselves
// into valid CBOR.
type Marshaler interface {
	MarshalCBOR() ([]byte, error)
}

// UnsupportedTypeError is returned by Marshal when attempting to encode value
// of an unsupported type.
type UnsupportedTypeError struct {
	Type reflect.Type
}

func (e *UnsupportedTypeError) Error() string {
	return "cbor: unsupported type: " + e.Type.String()
}

// SortMode identifies supported sorting order.
type SortMode int

const (
	// SortNone means no sorting.
	SortNone SortMode = 0

	// SortLengthFirst causes map keys or struct fields to be sorted such that:
	//     - If two keys have different lengths, the shorter one sorts earlier;
	//     - If two keys have the same length, the one with the lower value in
	//       (byte-wise) lexical order sorts earlier.
	// It is used in "Canonical CBOR" encoding in RFC 7049 3.9.
	SortLengthFirst SortMode = 1

	// SortBytewiseLexical causes map keys or struct fields to be sorted in the
	// bytewise lexicographic order of their deterministic CBOR encodings.
	// It is used in "CTAP2 Canonical CBOR" and "Core Deterministic Encoding"
	// in RFC 7049bis.
	SortBytewiseLexical SortMode = 2

	// SortCanonical is used in "Canonical CBOR" encoding in RFC 7049 3.9.
	SortCanonical SortMode = SortLengthFirst

	// SortCTAP2 is used in "CTAP2 Canonical CBOR".
	SortCTAP2 SortMode = SortBytewiseLexical

	// SortCoreDeterministic is used in "Core Deterministic Encoding" in RFC 7049bis.
	SortCoreDeterministic SortMode = SortBytewiseLexical

	maxSortMode SortMode = 3
)

func (sm SortMode) valid() bool {
	return sm < maxSortMode
}

// ShortestFloatMode specifies which floating-point format should
// be used as the shortest possible format for CBOR encoding.
// It is not used for encoding Infinity and NaN values.
type ShortestFloatMode int

const (
	// ShortestFloatNone makes float values encode without any conversion.
	// This is the default for ShortestFloatMode in v1.
	// E.g. a float32 in Go will encode to CBOR float32.  And
	// a float64 in Go will encode to CBOR float64.
	ShortestFloatNone ShortestFloatMode = iota

	// ShortestFloat16 specifies float16 as the shortest form that preserves value.
	// E.g. if float64 can convert to float32 while preserving value, then
	// encoding will also try to convert float32 to float16.  So a float64 might
	// encode as CBOR float64, float32 or float16 depending on the value.
	ShortestFloat16

	maxShortestFloat
)

func (sfm ShortestFloatMode) valid() bool {
	return sfm < maxShortestFloat
}

// NaNConvertMode specifies how to encode NaN and overrides ShortestFloatMode.
// ShortestFloatMode is not used for encoding Infinity and NaN values.
type NaNConvertMode int

const (
	// NaNConvert7e00 always encodes NaN to 0xf97e00 (CBOR float16 = 0x7e00).
	NaNConvert7e00 NaNConvertMode = iota

	// NaNConvertNone never modifies or converts NaN to other representations
	// (float64 NaN stays float64, etc. even if it can use float16 without losing
	// any bits).
	NaNConvertNone

	// NaNConvertPreserveSignal converts NaN to the smallest form that preserves
	// value (quiet bit + payload) as described in RFC 7049bis Draft 12.
	NaNConvertPreserveSignal

	// NaNConvertQuiet always forces quiet bit = 1 and shortest form that preserves
	// NaN payload.
	NaNConvertQuiet

	maxNaNConvert
)

func (ncm NaNConvertMode) valid() bool {
	return ncm < maxNaNConvert
}

// InfConvertMode specifies how to encode Infinity and overrides ShortestFloatMode.
// ShortestFloatMode is not used for encoding Infinity and NaN values.
type InfConvertMode int

const (
	// InfConvertFloat16 always converts Inf to lossless IEEE binary16 (float16).
	InfConvertFloat16 InfConvertMode = iota

	// InfConvertNone never converts (used by CTAP2 Canonical CBOR).
	InfConvertNone

	maxInfConvert
)

func (icm InfConvertMode) valid() bool {
	return icm < maxInfConvert
}

// TimeMode specifies how to encode time.Time values.
type TimeMode int

const (
	// TimeUnix causes time.Time to be encoded as epoch time in integer with second precision.
	TimeUnix TimeMode = iota

	// TimeUnixMicro causes time.Time to be encoded as epoch time in float-point rounded to microsecond precision.
	TimeUnixMicro

	// TimeUnixDynamic causes time.Time to be encoded as integer if time.Time doesn't have fractional seconds,
	// otherwise float-point rounded to microsecond precision.
	TimeUnixDynamic

	// TimeRFC3339 causes time.Time to be encoded as RFC3339 formatted string with second precision.
	TimeRFC3339

	// TimeRFC3339Nano causes time.Time to be encoded as RFC3339 formatted string with nanosecond precision.
	TimeRFC3339Nano

	maxTimeMode
)

func (tm TimeMode) valid() bool {
	return tm < maxTimeMode
}

// BigIntConvertMode specifies how to encode big.Int values.
type BigIntConvertMode int

const (
	// BigIntConvertShortest makes big.Int encode to CBOR integer if value fits.
	// E.g. if big.Int value can be converted to CBOR integer while preserving
	// value, encoder will encode it to CBOR interger (major type 0 or 1).
	BigIntConvertShortest BigIntConvertMode = iota

	// BigIntConvertNone makes big.Int encode to CBOR bignum (tag 2 or 3) without
	// converting it to another CBOR type.
	BigIntConvertNone

	maxBigIntConvert
)

func (bim BigIntConvertMode) valid() bool {
	return bim < maxBigIntConvert
}

// EncOptions specifies encoding options.
type EncOptions struct {
	// Sort specifies sorting order.
	Sort SortMode

	// ShortestFloat specifies the shortest floating-point encoding that preserves
	// the value being encoded.
	ShortestFloat ShortestFloatMode

	// NaNConvert specifies how to encode NaN and it overrides ShortestFloatMode.
	NaNConvert NaNConvertMode

	// InfConvert specifies how to encode Inf and it overrides ShortestFloatMode.
	InfConvert InfConvertMode

	// BigIntConvert specifies how to encode big.Int values.
	BigIntConvert BigIntConvertMode

	// Time specifies how to encode time.Time.
	Time TimeMode

	// TimeTag allows time.Time to be encoded with a tag number.
	// RFC3339 format gets tag number 0, and numeric epoch time tag number 1.
	TimeTag EncTagMode

	// IndefLength specifies whether to allow indefinite length CBOR items.
	IndefLength IndefLengthMode

	// TagsMd specifies whether to allow CBOR tags (major type 6).
	TagsMd TagsMode
}

// CanonicalEncOptions returns EncOptions for "Canonical CBOR" encoding,
// defined in RFC 7049 Section 3.9 with the following rules:
//
//     1. "Integers must be as small as possible."
//     2. "The expression of lengths in major types 2 through 5 must be as short as possible."
//     3. The keys in every map must be sorted in length-first sorting order.
//        See SortLengthFirst for details.
//     4. "Indefinite-length items must be made into definite-length items."
//     5. "If a protocol allows for IEEE floats, then additional canonicalization rules might
//        need to be added.  One example rule might be to have all floats start as a 64-bit
//        float, then do a test conversion to a 32-bit float; if the result is the same numeric
//        value, use the shorter value and repeat the process with a test conversion to a
//        16-bit float.  (This rule selects 16-bit float for positive and negative Infinity
//        as well.)  Also, there are many representations for NaN.  If NaN is an allowed value,
//        it must always be represented as 0xf97e00."
//
func CanonicalEncOptions() EncOptions {
	return EncOptions{
		Sort:          SortCanonical,
		ShortestFloat: ShortestFloat16,
		NaNConvert:    NaNConvert7e00,
		InfConvert:    InfConvertFloat16,
		IndefLength:   IndefLengthForbidden,
	}
}

// CTAP2EncOptions returns EncOptions for "CTAP2 Canonical CBOR" encoding,
// defined in CTAP specification, with the following rules:
//
//     1. "Integers must be encoded as small as possible."
//     2. "The representations of any floating-point values are not changed."
//     3. "The expression of lengths in major types 2 through 5 must be as short as possible."
//     4. "Indefinite-length items must be made into definite-length items.""
//     5. The keys in every map must be sorted in bytewise lexicographic order.
//        See SortBytewiseLexical for details.
//     6. "Tags as defined in Section 2.4 in [RFC7049] MUST NOT be present."
//
func CTAP2EncOptions() EncOptions {
	return EncOptions{
		Sort:          SortCTAP2,
		ShortestFloat: ShortestFloatNone,
		NaNConvert:    NaNConvertNone,
		InfConvert:    InfConvertNone,
		IndefLength:   IndefLengthForbidden,
		TagsMd:        TagsForbidden,
	}
}

// CoreDetEncOptions returns EncOptions for "Core Deterministic" encoding,
// defined in RFC 7049bis with the following rules:
//
//     1. "Preferred serialization MUST be used. In particular, this means that arguments
//        (see Section 3) for integers, lengths in major types 2 through 5, and tags MUST
//        be as short as possible"
//        "Floating point values also MUST use the shortest form that preserves the value"
//     2. "Indefinite-length items MUST NOT appear."
//     3. "The keys in every map MUST be sorted in the bytewise lexicographic order of
//        their deterministic encodings."
//
func CoreDetEncOptions() EncOptions {
	return EncOptions{
		Sort:          SortCoreDeterministic,
		ShortestFloat: ShortestFloat16,
		NaNConvert:    NaNConvert7e00,
		InfConvert:    InfConvertFloat16,
		IndefLength:   IndefLengthForbidden,
	}
}

// PreferredUnsortedEncOptions returns EncOptions for "Preferred Serialization" encoding,
// defined in RFC 7049bis with the following rules:
//
//     1. "The preferred serialization always uses the shortest form of representing the argument
//        (Section 3);"
//     2. "it also uses the shortest floating-point encoding that preserves the value being
//        encoded (see Section 5.5)."
//        "The preferred encoding for a floating-point value is the shortest floating-point encoding
//        that preserves its value, e.g., 0xf94580 for the number 5.5, and 0xfa45ad9c00 for the
//        number 5555.5, unless the CBOR-based protocol specifically excludes the use of the shorter
//        floating-point encodings. For NaN values, a shorter encoding is preferred if zero-padding
//        the shorter significand towards the right reconstitutes the original NaN value (for many
//        applications, the single NaN encoding 0xf97e00 will suffice)."
//     3. "Definite length encoding is preferred whenever the length is known at the time the
//        serialization of the item starts."
//
func PreferredUnsortedEncOptions() EncOptions {
	return EncOptions{
		Sort:          SortNone,
		ShortestFloat: ShortestFloat16,
		NaNConvert:    NaNConvert7e00,
		InfConvert:    InfConvertFloat16,
	}
}

// EncMode returns EncMode with immutable options and no tags (safe for concurrency).
func (opts EncOptions) EncMode() (EncMode, error) {
	return opts.encMode()
}

// EncModeWithTags returns EncMode with options and tags that are both immutable (safe for concurrency).
func (opts EncOptions) EncModeWithTags(tags TagSet) (EncMode, error) {
	if opts.TagsMd == TagsForbidden {
		return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden")
	}
	if tags == nil {
		return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet")
	}
	em, err := opts.encMode()
	if err != nil {
		return nil, err
	}
	// Copy tags
	ts := tagSet(make(map[reflect.Type]*tagItem))
	syncTags := tags.(*syncTagSet)
	syncTags.RLock()
	for contentType, tag := range syncTags.t {
		if tag.opts.EncTag != EncTagNone {
			ts[contentType] = tag
		}
	}
	syncTags.RUnlock()
	if len(ts) > 0 {
		em.tags = ts
	}
	return em, nil
}

// EncModeWithSharedTags returns EncMode with immutable options and mutable shared tags (safe for concurrency).
func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) {
	if opts.TagsMd == TagsForbidden {
		return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden")
	}
	if tags == nil {
		return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet")
	}
	em, err := opts.encMode()
	if err != nil {
		return nil, err
	}
	em.tags = tags
	return em, nil
}

func (opts EncOptions) encMode() (*encMode, error) {
	if !opts.Sort.valid() {
		return nil, errors.New("cbor: invalid SortMode " + strconv.Itoa(int(opts.Sort)))
	}
	if !opts.ShortestFloat.valid() {
		return nil, errors.New("cbor: invalid ShortestFloatMode " + strconv.Itoa(int(opts.ShortestFloat)))
	}
	if !opts.NaNConvert.valid() {
		return nil, errors.New("cbor: invalid NaNConvertMode " + strconv.Itoa(int(opts.NaNConvert)))
	}
	if !opts.InfConvert.valid() {
		return nil, errors.New("cbor: invalid InfConvertMode " + strconv.Itoa(int(opts.InfConvert)))
	}
	if !opts.BigIntConvert.valid() {
		return nil, errors.New("cbor: invalid BigIntConvertMode " + strconv.Itoa(int(opts.BigIntConvert)))
	}
	if !opts.Time.valid() {
		return nil, errors.New("cbor: invalid TimeMode " + strconv.Itoa(int(opts.Time)))
	}
	if !opts.TimeTag.valid() {
		return nil, errors.New("cbor: invalid TimeTag " + strconv.Itoa(int(opts.TimeTag)))
	}
	if !opts.IndefLength.valid() {
		return nil, errors.New("cbor: invalid IndefLength " + strconv.Itoa(int(opts.IndefLength)))
	}
	if !opts.TagsMd.valid() {
		return nil, errors.New("cbor: invalid TagsMd " + strconv.Itoa(int(opts.TagsMd)))
	}
	if opts.TagsMd == TagsForbidden && opts.TimeTag == EncTagRequired {
		return nil, errors.New("cbor: cannot set TagsMd to TagsForbidden when TimeTag is EncTagRequired")
	}
	em := encMode{
		sort:          opts.Sort,
		shortestFloat: opts.ShortestFloat,
		nanConvert:    opts.NaNConvert,
		infConvert:    opts.InfConvert,
		bigIntConvert: opts.BigIntConvert,
		time:          opts.Time,
		timeTag:       opts.TimeTag,
		indefLength:   opts.IndefLength,
		tagsMd:        opts.TagsMd,
	}
	return &em, nil
}

// EncMode is the main interface for CBOR encoding.
type EncMode interface {
	Marshal(v interface{}) ([]byte, error)
	NewEncoder(w io.Writer) *Encoder
	EncOptions() EncOptions
}

type encMode struct {
	tags          tagProvider
	sort          SortMode
	shortestFloat ShortestFloatMode
	nanConvert    NaNConvertMode
	infConvert    InfConvertMode
	bigIntConvert BigIntConvertMode
	time          TimeMode
	timeTag       EncTagMode
	indefLength   IndefLengthMode
	tagsMd        TagsMode
}

var defaultEncMode = &encMode{}

// EncOptions returns user specified options used to create this EncMode.
func (em *encMode) EncOptions() EncOptions {
	return EncOptions{
		Sort:          em.sort,
		ShortestFloat: em.shortestFloat,
		NaNConvert:    em.nanConvert,
		InfConvert:    em.infConvert,
		BigIntConvert: em.bigIntConvert,
		Time:          em.time,
		TimeTag:       em.timeTag,
		IndefLength:   em.indefLength,
		TagsMd:        em.tagsMd,
	}
}

func (em *encMode) encTagBytes(t reflect.Type) []byte {
	if em.tags != nil {
		if tagItem := em.tags.getTagItemFromType(t); tagItem != nil {
			return tagItem.cborTagNum
		}
	}
	return nil
}

// Marshal returns the CBOR encoding of v using em encoding mode.
//
// See the documentation for Marshal for details.
func (em *encMode) Marshal(v interface{}) ([]byte, error) {
	e := getEncoderBuffer()

	if err := encode(e, em, reflect.ValueOf(v)); err != nil {
		putEncoderBuffer(e)
		return nil, err
	}

	buf := make([]byte, e.Len())
	copy(buf, e.Bytes())

	putEncoderBuffer(e)
	return buf, nil
}

// NewEncoder returns a new encoder that writes to w using em EncMode.
func (em *encMode) NewEncoder(w io.Writer) *Encoder {
	return &Encoder{w: w, em: em, e: getEncoderBuffer()}
}

type encoderBuffer struct {
	bytes.Buffer
	scratch [16]byte
}

// encoderBufferPool caches unused encoderBuffer objects for later reuse.
var encoderBufferPool = sync.Pool{
	New: func() interface{} {
		e := new(encoderBuffer)
		e.Grow(32) // TODO: make this configurable
		return e
	},
}

func getEncoderBuffer() *encoderBuffer {
	return encoderBufferPool.Get().(*encoderBuffer)
}

func putEncoderBuffer(e *encoderBuffer) {
	e.Reset()
	encoderBufferPool.Put(e)
}

type encodeFunc func(e *encoderBuffer, em *encMode, v reflect.Value) error
type isEmptyFunc func(v reflect.Value) (empty bool, err error)

var (
	cborFalse            = []byte{0xf4}
	cborTrue             = []byte{0xf5}
	cborNil              = []byte{0xf6}
	cborNaN              = []byte{0xf9, 0x7e, 0x00}
	cborPositiveInfinity = []byte{0xf9, 0x7c, 0x00}
	cborNegativeInfinity = []byte{0xf9, 0xfc, 0x00}
)

func encode(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if !v.IsValid() {
		// v is zero value
		e.Write(cborNil)
		return nil
	}
	vt := v.Type()
	f, _ := getEncodeFunc(vt)
	if f == nil {
		return &UnsupportedTypeError{vt}
	}

	return f(e, em, v)
}

func encodeBool(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	b := cborFalse
	if v.Bool() {
		b = cborTrue
	}
	e.Write(b)
	return nil
}

func encodeInt(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	i := v.Int()
	if i >= 0 {
		encodeHead(e, byte(cborTypePositiveInt), uint64(i))
		return nil
	}
	i = i*(-1) - 1
	encodeHead(e, byte(cborTypeNegativeInt), uint64(i))
	return nil
}

func encodeUint(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	encodeHead(e, byte(cborTypePositiveInt), v.Uint())
	return nil
}

func encodeFloat(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	f64 := v.Float()
	if math.IsNaN(f64) {
		return encodeNaN(e, em, v)
	}
	if math.IsInf(f64, 0) {
		return encodeInf(e, em, v)
	}
	fopt := em.shortestFloat
	if v.Kind() == reflect.Float64 && (fopt == ShortestFloatNone || cannotFitFloat32(f64)) {
		// Encode float64
		// Don't use encodeFloat64() because it cannot be inlined.
		e.scratch[0] = byte(cborTypePrimitives) | byte(27)
		binary.BigEndian.PutUint64(e.scratch[1:], math.Float64bits(f64))
		e.Write(e.scratch[:9])
		return nil
	}

	f32 := float32(f64)
	if fopt == ShortestFloat16 {
		var f16 float16.Float16
		p := float16.PrecisionFromfloat32(f32)
		if p == float16.PrecisionExact {
			// Roundtrip float32->float16->float32 test isn't needed.
			f16 = float16.Fromfloat32(f32)
		} else if p == float16.PrecisionUnknown {
			// Try roundtrip float32->float16->float32 to determine if float32 can fit into float16.
			f16 = float16.Fromfloat32(f32)
			if f16.Float32() == f32 {
				p = float16.PrecisionExact
			}
		}
		if p == float16.PrecisionExact {
			// Encode float16
			// Don't use encodeFloat16() because it cannot be inlined.
			e.scratch[0] = byte(cborTypePrimitives) | byte(25)
			binary.BigEndian.PutUint16(e.scratch[1:], uint16(f16))
			e.Write(e.scratch[:3])
			return nil
		}
	}

	// Encode float32
	// Don't use encodeFloat32() because it cannot be inlined.
	e.scratch[0] = byte(cborTypePrimitives) | byte(26)
	binary.BigEndian.PutUint32(e.scratch[1:], math.Float32bits(f32))
	e.Write(e.scratch[:5])
	return nil
}

func encodeInf(e *encoderBuffer, em *encMode, v reflect.Value) error {
	f64 := v.Float()
	if em.infConvert == InfConvertFloat16 {
		if f64 > 0 {
			e.Write(cborPositiveInfinity)
		} else {
			e.Write(cborNegativeInfinity)
		}
		return nil
	}
	if v.Kind() == reflect.Float64 {
		return encodeFloat64(e, f64)
	}
	return encodeFloat32(e, float32(f64))
}

func encodeNaN(e *encoderBuffer, em *encMode, v reflect.Value) error {
	switch em.nanConvert {
	case NaNConvert7e00:
		e.Write(cborNaN)
		return nil

	case NaNConvertNone:
		if v.Kind() == reflect.Float64 {
			return encodeFloat64(e, v.Float())
		}
		f32 := float32NaNFromReflectValue(v)
		return encodeFloat32(e, f32)

	default: // NaNConvertPreserveSignal, NaNConvertQuiet
		if v.Kind() == reflect.Float64 {
			f64 := v.Float()
			f64bits := math.Float64bits(f64)
			if em.nanConvert == NaNConvertQuiet && f64bits&(1<<51) == 0 {
				f64bits |= 1 << 51 // Set quiet bit = 1
				f64 = math.Float64frombits(f64bits)
			}
			// The lower 29 bits are dropped when converting from float64 to float32.
			if f64bits&0x1fffffff != 0 {
				// Encode NaN as float64 because dropped coef bits from float64 to float32 are not all 0s.
				return encodeFloat64(e, f64)
			}
			// Create float32 from float64 manually because float32(f64) always turns on NaN's quiet bits.
			sign := uint32(f64bits>>32) & (1 << 31)
			exp := uint32(0x7f800000)
			coef := uint32((f64bits & 0xfffffffffffff) >> 29)
			f32bits := sign | exp | coef
			f32 := math.Float32frombits(f32bits)
			// The lower 13 bits are dropped when converting from float32 to float16.
			if f32bits&0x1fff != 0 {
				// Encode NaN as float32 because dropped coef bits from float32 to float16 are not all 0s.
				return encodeFloat32(e, f32)
			}
			// Encode NaN as float16
			f16, _ := float16.FromNaN32ps(f32) // Ignore err because it only returns error when f32 is not a NaN.
			return encodeFloat16(e, f16)
		}

		f32 := float32NaNFromReflectValue(v)
		f32bits := math.Float32bits(f32)
		if em.nanConvert == NaNConvertQuiet && f32bits&(1<<22) == 0 {
			f32bits |= 1 << 22 // Set quiet bit = 1
			f32 = math.Float32frombits(f32bits)
		}
		// The lower 13 bits are dropped coef bits when converting from float32 to float16.
		if f32bits&0x1fff != 0 {
			// Encode NaN as float32 because dropped coef bits from float32 to float16 are not all 0s.
			return encodeFloat32(e, f32)
		}
		f16, _ := float16.FromNaN32ps(f32) // Ignore err because it only returns error when f32 is not a NaN.
		return encodeFloat16(e, f16)
	}
}

func encodeFloat16(e *encoderBuffer, f16 float16.Float16) error {
	e.scratch[0] = byte(cborTypePrimitives) | byte(25)
	binary.BigEndian.PutUint16(e.scratch[1:], uint16(f16))
	e.Write(e.scratch[:3])
	return nil
}

func encodeFloat32(e *encoderBuffer, f32 float32) error {
	e.scratch[0] = byte(cborTypePrimitives) | byte(26)
	binary.BigEndian.PutUint32(e.scratch[1:], math.Float32bits(f32))
	e.Write(e.scratch[:5])
	return nil
}

func encodeFloat64(e *encoderBuffer, f64 float64) error {
	e.scratch[0] = byte(cborTypePrimitives) | byte(27)
	binary.BigEndian.PutUint64(e.scratch[1:], math.Float64bits(f64))
	e.Write(e.scratch[:9])
	return nil
}

func encodeByteString(e *encoderBuffer, em *encMode, v reflect.Value) error {
	vk := v.Kind()
	if vk == reflect.Slice && v.IsNil() {
		e.Write(cborNil)
		return nil
	}
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	slen := v.Len()
	if slen == 0 {
		return e.WriteByte(byte(cborTypeByteString))
	}
	encodeHead(e, byte(cborTypeByteString), uint64(slen))
	if vk == reflect.Array {
		for i := 0; i < slen; i++ {
			e.WriteByte(byte(v.Index(i).Uint()))
		}
		return nil
	}
	e.Write(v.Bytes())
	return nil
}

func encodeString(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	s := v.String()
	encodeHead(e, byte(cborTypeTextString), uint64(len(s)))
	e.WriteString(s)
	return nil
}

type arrayEncodeFunc struct {
	f encodeFunc
}

func (ae arrayEncodeFunc) encode(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if v.Kind() == reflect.Slice && v.IsNil() {
		e.Write(cborNil)
		return nil
	}
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	alen := v.Len()
	if alen == 0 {
		return e.WriteByte(byte(cborTypeArray))
	}
	encodeHead(e, byte(cborTypeArray), uint64(alen))
	for i := 0; i < alen; i++ {
		if err := ae.f(e, em, v.Index(i)); err != nil {
			return err
		}
	}
	return nil
}

type mapEncodeFunc struct {
	kf, ef encodeFunc
}

func (me mapEncodeFunc) encode(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if v.IsNil() {
		e.Write(cborNil)
		return nil
	}
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}
	mlen := v.Len()
	if mlen == 0 {
		return e.WriteByte(byte(cborTypeMap))
	}
	if em.sort != SortNone {
		return me.encodeCanonical(e, em, v)
	}
	encodeHead(e, byte(cborTypeMap), uint64(mlen))
	iter := v.MapRange()
	for iter.Next() {
		if err := me.kf(e, em, iter.Key()); err != nil {
			return err
		}
		if err := me.ef(e, em, iter.Value()); err != nil {
			return err
		}
	}
	return nil
}

type keyValue struct {
	keyCBORData, keyValueCBORData []byte
	keyLen, keyValueLen           int
}

type bytewiseKeyValueSorter struct {
	kvs []keyValue
}

func (x *bytewiseKeyValueSorter) Len() int {
	return len(x.kvs)
}

func (x *bytewiseKeyValueSorter) Swap(i, j int) {
	x.kvs[i], x.kvs[j] = x.kvs[j], x.kvs[i]
}

func (x *bytewiseKeyValueSorter) Less(i, j int) bool {
	return bytes.Compare(x.kvs[i].keyCBORData, x.kvs[j].keyCBORData) <= 0
}

type lengthFirstKeyValueSorter struct {
	kvs []keyValue
}

func (x *lengthFirstKeyValueSorter) Len() int {
	return len(x.kvs)
}

func (x *lengthFirstKeyValueSorter) Swap(i, j int) {
	x.kvs[i], x.kvs[j] = x.kvs[j], x.kvs[i]
}

func (x *lengthFirstKeyValueSorter) Less(i, j int) bool {
	if len(x.kvs[i].keyCBORData) != len(x.kvs[j].keyCBORData) {
		return len(x.kvs[i].keyCBORData) < len(x.kvs[j].keyCBORData)
	}
	return bytes.Compare(x.kvs[i].keyCBORData, x.kvs[j].keyCBORData) <= 0
}

var keyValuePool = sync.Pool{}

func getKeyValues(length int) *[]keyValue {
	v := keyValuePool.Get()
	if v == nil {
		y := make([]keyValue, length)
		return &y
	}
	x := v.(*[]keyValue)
	if cap(*x) >= length {
		*x = (*x)[:length]
		return x
	}
	// []keyValue from the pool does not have enough capacity.
	// Return it back to the pool and create a new one.
	keyValuePool.Put(x)
	y := make([]keyValue, length)
	return &y
}

func putKeyValues(x *[]keyValue) {
	*x = (*x)[:0]
	keyValuePool.Put(x)
}

func (me mapEncodeFunc) encodeCanonical(e *encoderBuffer, em *encMode, v reflect.Value) error {
	kve := getEncoderBuffer()     // accumulated cbor encoded key-values
	kvsp := getKeyValues(v.Len()) // for sorting keys
	kvs := *kvsp
	iter := v.MapRange()
	for i := 0; iter.Next(); i++ {
		off := kve.Len()
		if err := me.kf(kve, em, iter.Key()); err != nil {
			putEncoderBuffer(kve)
			putKeyValues(kvsp)
			return err
		}
		n1 := kve.Len() - off
		if err := me.ef(kve, em, iter.Value()); err != nil {
			putEncoderBuffer(kve)
			putKeyValues(kvsp)
			return err
		}
		n2 := kve.Len() - off
		// Save key and keyvalue length to create slice later.
		kvs[i] = keyValue{keyLen: n1, keyValueLen: n2}
	}

	b := kve.Bytes()
	for i, off := 0, 0; i < len(kvs); i++ {
		kvs[i].keyCBORData = b[off : off+kvs[i].keyLen]
		kvs[i].keyValueCBORData = b[off : off+kvs[i].keyValueLen]
		off += kvs[i].keyValueLen
	}

	if em.sort == SortBytewiseLexical {
		sort.Sort(&bytewiseKeyValueSorter{kvs})
	} else {
		sort.Sort(&lengthFirstKeyValueSorter{kvs})
	}

	encodeHead(e, byte(cborTypeMap), uint64(len(kvs)))
	for i := 0; i < len(kvs); i++ {
		e.Write(kvs[i].keyValueCBORData)
	}

	putEncoderBuffer(kve)
	putKeyValues(kvsp)
	return nil
}

func encodeStructToArray(e *encoderBuffer, em *encMode, v reflect.Value) (err error) {
	structType, err := getEncodingStructType(v.Type())
	if err != nil {
		return err
	}

	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}

	flds := structType.fields

	encodeHead(e, byte(cborTypeArray), uint64(len(flds)))
	for i := 0; i < len(flds); i++ {
		f := flds[i]

		var fv reflect.Value
		if len(f.idx) == 1 {
			fv = v.Field(f.idx[0])
		} else {
			// Get embedded field value.  No error is expected.
			fv, _ = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) {
				// Write CBOR nil for null pointer to embedded struct
				e.Write(cborNil)
				return reflect.Value{}, nil
			})
			if !fv.IsValid() {
				continue
			}
		}

		if err := f.ef(e, em, fv); err != nil {
			return err
		}
	}
	return nil
}

func encodeFixedLengthStruct(e *encoderBuffer, em *encMode, v reflect.Value, flds fields) error {
	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}

	encodeHead(e, byte(cborTypeMap), uint64(len(flds)))

	for i := 0; i < len(flds); i++ {
		f := flds[i]
		e.Write(f.cborName)

		fv := v.Field(f.idx[0])
		if err := f.ef(e, em, fv); err != nil {
			return err
		}
	}

	return nil
}

func encodeStruct(e *encoderBuffer, em *encMode, v reflect.Value) (err error) {
	structType, err := getEncodingStructType(v.Type())
	if err != nil {
		return err
	}

	flds := structType.getFields(em)

	if structType.fixedLength {
		return encodeFixedLengthStruct(e, em, v, flds)
	}

	kve := getEncoderBuffer() // encode key-value pairs based on struct field tag options
	kvcount := 0
	for i := 0; i < len(flds); i++ {
		f := flds[i]

		var fv reflect.Value
		if len(f.idx) == 1 {
			fv = v.Field(f.idx[0])
		} else {
			// Get embedded field value.  No error is expected.
			fv, _ = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) {
				// Skip null pointer to embedded struct
				return reflect.Value{}, nil
			})
			if !fv.IsValid() {
				continue
			}
		}

		if f.omitEmpty {
			empty, err := f.ief(fv)
			if err != nil {
				putEncoderBuffer(kve)
				return err
			}
			if empty {
				continue
			}
		}

		kve.Write(f.cborName)

		if err := f.ef(kve, em, fv); err != nil {
			putEncoderBuffer(kve)
			return err
		}
		kvcount++
	}

	if b := em.encTagBytes(v.Type()); b != nil {
		e.Write(b)
	}

	encodeHead(e, byte(cborTypeMap), uint64(kvcount))
	e.Write(kve.Bytes())

	putEncoderBuffer(kve)
	return nil
}

func encodeIntf(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if v.IsNil() {
		e.Write(cborNil)
		return nil
	}
	return encode(e, em, v.Elem())
}

func encodeTime(e *encoderBuffer, em *encMode, v reflect.Value) error {
	t := v.Interface().(time.Time)
	if t.IsZero() {
		e.Write(cborNil) // Even if tag is required, encode as CBOR null.
		return nil
	}
	if em.timeTag == EncTagRequired {
		tagNumber := 1
		if em.time == TimeRFC3339 || em.time == TimeRFC3339Nano {
			tagNumber = 0
		}
		encodeHead(e, byte(cborTypeTag), uint64(tagNumber))
	}
	switch em.time {
	case TimeUnix:
		secs := t.Unix()
		return encodeInt(e, em, reflect.ValueOf(secs))
	case TimeUnixMicro:
		t = t.UTC().Round(time.Microsecond)
		f := float64(t.UnixNano()) / 1e9
		return encodeFloat(e, em, reflect.ValueOf(f))
	case TimeUnixDynamic:
		t = t.UTC().Round(time.Microsecond)
		secs, nsecs := t.Unix(), uint64(t.Nanosecond())
		if nsecs == 0 {
			return encodeInt(e, em, reflect.ValueOf(secs))
		}
		f := float64(secs) + float64(nsecs)/1e9
		return encodeFloat(e, em, reflect.ValueOf(f))
	case TimeRFC3339:
		s := t.Format(time.RFC3339)
		return encodeString(e, em, reflect.ValueOf(s))
	default: // TimeRFC3339Nano
		s := t.Format(time.RFC3339Nano)
		return encodeString(e, em, reflect.ValueOf(s))
	}
}

func encodeBigInt(e *encoderBuffer, em *encMode, v reflect.Value) error {
	vbi := v.Interface().(big.Int)
	sign := vbi.Sign()
	bi := new(big.Int).SetBytes(vbi.Bytes()) // bi is absolute value of v
	if sign < 0 {
		// For negative number, convert to CBOR encoded number (-v-1).
		bi.Sub(bi, big.NewInt(1))
	}

	if em.bigIntConvert == BigIntConvertShortest {
		if bi.IsUint64() {
			if sign >= 0 {
				// Encode as CBOR pos int (major type 0)
				encodeHead(e, byte(cborTypePositiveInt), bi.Uint64())
				return nil
			}
			// Encode as CBOR neg int (major type 1)
			encodeHead(e, byte(cborTypeNegativeInt), bi.Uint64())
			return nil
		}
	}

	tagNum := 2
	if sign < 0 {
		tagNum = 3
	}
	// Write tag number
	encodeHead(e, byte(cborTypeTag), uint64(tagNum))
	// Write bignum byte string
	b := bi.Bytes()
	encodeHead(e, byte(cborTypeByteString), uint64(len(b)))
	e.Write(b)
	return nil
}

func encodeBinaryMarshalerType(e *encoderBuffer, em *encMode, v reflect.Value) error {
	vt := v.Type()
	m, ok := v.Interface().(encoding.BinaryMarshaler)
	if !ok {
		pv := reflect.New(vt)
		pv.Elem().Set(v)
		m = pv.Interface().(encoding.BinaryMarshaler)
	}
	data, err := m.MarshalBinary()
	if err != nil {
		return err
	}
	if b := em.encTagBytes(vt); b != nil {
		e.Write(b)
	}
	encodeHead(e, byte(cborTypeByteString), uint64(len(data)))
	e.Write(data)
	return nil
}

func encodeMarshalerType(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if em.tagsMd == TagsForbidden && v.Type() == typeRawTag {
		return errors.New("cbor: cannot encode cbor.RawTag when TagsMd is TagsForbidden")
	}
	m, ok := v.Interface().(Marshaler)
	if !ok {
		pv := reflect.New(v.Type())
		pv.Elem().Set(v)
		m = pv.Interface().(Marshaler)
	}
	data, err := m.MarshalCBOR()
	if err != nil {
		return err
	}
	e.Write(data)
	return nil
}

func encodeTag(e *encoderBuffer, em *encMode, v reflect.Value) error {
	if em.tagsMd == TagsForbidden {
		return errors.New("cbor: cannot encode cbor.Tag when TagsMd is TagsForbidden")
	}

	t := v.Interface().(Tag)

	if t.Number == 0 && t.Content == nil {
		// Marshal uninitialized cbor.Tag
		e.Write(cborNil)
		return nil
	}

	// Marshal tag number
	encodeHead(e, byte(cborTypeTag), t.Number)

	// Marshal tag content
	if err := encode(e, em, reflect.ValueOf(t.Content)); err != nil {
		return err
	}

	return nil
}

func encodeHead(e *encoderBuffer, t byte, n uint64) {
	if n <= 23 {
		e.WriteByte(t | byte(n))
		return
	}
	if n <= math.MaxUint8 {
		e.scratch[0] = t | byte(24)
		e.scratch[1] = byte(n)
		e.Write(e.scratch[:2])
		return
	}
	if n <= math.MaxUint16 {
		e.scratch[0] = t | byte(25)
		binary.BigEndian.PutUint16(e.scratch[1:], uint16(n))
		e.Write(e.scratch[:3])
		return
	}
	if n <= math.MaxUint32 {
		e.scratch[0] = t | byte(26)
		binary.BigEndian.PutUint32(e.scratch[1:], uint32(n))
		e.Write(e.scratch[:5])
		return
	}
	e.scratch[0] = t | byte(27)
	binary.BigEndian.PutUint64(e.scratch[1:], n)
	e.Write(e.scratch[:9])
}

var (
	typeMarshaler       = reflect.TypeOf((*Marshaler)(nil)).Elem()
	typeBinaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
	typeRawMessage      = reflect.TypeOf(RawMessage(nil))
)

func getEncodeFuncInternal(t reflect.Type) (encodeFunc, isEmptyFunc) {
	k := t.Kind()
	if k == reflect.Ptr {
		return getEncodeIndirectValueFunc(t), isEmptyPtr
	}
	switch t {
	case typeTag:
		return encodeTag, alwaysNotEmpty
	case typeTime:
		return encodeTime, alwaysNotEmpty
	case typeBigInt:
		return encodeBigInt, alwaysNotEmpty
	case typeRawMessage:
		return encodeMarshalerType, isEmptySlice
	}
	if reflect.PtrTo(t).Implements(typeMarshaler) {
		return encodeMarshalerType, alwaysNotEmpty
	}
	if reflect.PtrTo(t).Implements(typeBinaryMarshaler) {
		return encodeBinaryMarshalerType, isEmptyBinaryMarshaler
	}
	switch k {
	case reflect.Bool:
		return encodeBool, isEmptyBool
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return encodeInt, isEmptyInt
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return encodeUint, isEmptyUint
	case reflect.Float32, reflect.Float64:
		return encodeFloat, isEmptyFloat
	case reflect.String:
		return encodeString, isEmptyString
	case reflect.Slice, reflect.Array:
		if t.Elem().Kind() == reflect.Uint8 {
			return encodeByteString, isEmptySlice
		}
		f, _ := getEncodeFunc(t.Elem())
		if f == nil {
			return nil, nil
		}
		return arrayEncodeFunc{f: f}.encode, isEmptySlice
	case reflect.Map:
		kf, _ := getEncodeFunc(t.Key())
		ef, _ := getEncodeFunc(t.Elem())
		if kf == nil || ef == nil {
			return nil, nil
		}
		return mapEncodeFunc{kf: kf, ef: ef}.encode, isEmptyMap
	case reflect.Struct:
		// Get struct's special field "_" tag options
		if f, ok := t.FieldByName("_"); ok {
			tag := f.Tag.Get("cbor")
			if tag != "-" {
				if hasToArrayOption(tag) {
					return encodeStructToArray, isEmptyStruct
				}
			}
		}
		return encodeStruct, isEmptyStruct
	case reflect.Interface:
		return encodeIntf, isEmptyIntf
	}
	return nil, nil
}

func getEncodeIndirectValueFunc(t reflect.Type) encodeFunc {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	f, _ := getEncodeFunc(t)
	if f == nil {
		return nil
	}
	return func(e *encoderBuffer, em *encMode, v reflect.Value) error {
		for v.Kind() == reflect.Ptr && !v.IsNil() {
			v = v.Elem()
		}
		if v.Kind() == reflect.Ptr && v.IsNil() {
			e.Write(cborNil)
			return nil
		}
		return f(e, em, v)
	}
}

func alwaysNotEmpty(v reflect.Value) (empty bool, err error) {
	return false, nil
}

func isEmptyBool(v reflect.Value) (bool, error) {
	return !v.Bool(), nil
}

func isEmptyInt(v reflect.Value) (bool, error) {
	return v.Int() == 0, nil
}

func isEmptyUint(v reflect.Value) (bool, error) {
	return v.Uint() == 0, nil
}

func isEmptyFloat(v reflect.Value) (bool, error) {
	return v.Float() == 0.0, nil
}

func isEmptyString(v reflect.Value) (bool, error) {
	return v.Len() == 0, nil
}

func isEmptySlice(v reflect.Value) (bool, error) {
	return v.Len() == 0, nil
}

func isEmptyMap(v reflect.Value) (bool, error) {
	return v.Len() == 0, nil
}

func isEmptyPtr(v reflect.Value) (bool, error) {
	return v.IsNil(), nil
}

func isEmptyIntf(v reflect.Value) (bool, error) {
	return v.IsNil(), nil
}

func isEmptyStruct(v reflect.Value) (bool, error) {
	structType, err := getEncodingStructType(v.Type())
	if err != nil {
		return false, err
	}

	if structType.toArray {
		return len(structType.fields) == 0, nil
	}

	if len(structType.fields) > len(structType.omitEmptyFieldsIdx) {
		return false, nil
	}

	for _, i := range structType.omitEmptyFieldsIdx {
		f := structType.fields[i]

		// Get field value
		var fv reflect.Value
		if len(f.idx) == 1 {
			fv = v.Field(f.idx[0])
		} else {
			// Get embedded field value.  No error is expected.
			fv, _ = getFieldValue(v, f.idx, func(v reflect.Value) (reflect.Value, error) {
				// Skip null pointer to embedded struct
				return reflect.Value{}, nil
			})
			if !fv.IsValid() {
				continue
			}
		}

		empty, err := f.ief(fv)
		if err != nil {
			return false, err
		}
		if !empty {
			return false, nil
		}
	}
	return true, nil
}

func isEmptyBinaryMarshaler(v reflect.Value) (bool, error) {
	m, ok := v.Interface().(encoding.BinaryMarshaler)
	if !ok {
		pv := reflect.New(v.Type())
		pv.Elem().Set(v)
		m = pv.Interface().(encoding.BinaryMarshaler)
	}
	data, err := m.MarshalBinary()
	if err != nil {
		return false, err
	}
	return len(data) == 0, nil
}

func cannotFitFloat32(f64 float64) bool {
	f32 := float32(f64)
	return float64(f32) != f64
}

// float32NaNFromReflectValue extracts float32 NaN from reflect.Value while preserving NaN's quiet bit.
func float32NaNFromReflectValue(v reflect.Value) float32 {
	// Keith Randall's workaround for issue https://github.com/golang/go/issues/36400
	p := reflect.New(v.Type())
	p.Elem().Set(v)
	f32 := p.Convert(reflect.TypeOf((*float32)(nil))).Elem().Interface().(float32)
	return f32
}
