// 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"
	"fmt"
	"io"
	"reflect"
	"testing"
)

const rounds = 100

type claims struct {
	Iss string `cbor:"1,keyasint"`
	Sub string `cbor:"2,keyasint"`
	Aud string `cbor:"3,keyasint"`
	Exp int    `cbor:"4,keyasint"`
	Nbf int    `cbor:"5,keyasint"`
	Iat int    `cbor:"6,keyasint"`
	Cti []byte `cbor:"7,keyasint"`
}

type coseHeader struct {
	Alg int    `cbor:"1,keyasint,omitempty"`
	Kid []byte `cbor:"4,keyasint,omitempty"`
	IV  []byte `cbor:"5,keyasint,omitempty"`
}

type macedCOSE struct {
	_           struct{} `cbor:",toarray"`
	Protected   []byte
	Unprotected coseHeader
	Payload     []byte
	Tag         []byte
}

type coseKey struct {
	Kty       int        `cbor:"1,keyasint,omitempty"`
	Kid       []byte     `cbor:"2,keyasint,omitempty"`
	Alg       int        `cbor:"3,keyasint,omitempty"`
	KeyOpts   int        `cbor:"4,keyasint,omitempty"`
	IV        []byte     `cbor:"5,keyasint,omitempty"`
	CrvOrNOrK RawMessage `cbor:"-1,keyasint,omitempty"` // K for symmetric keys, Crv for elliptic curve keys, N for RSA modulus
	XOrE      RawMessage `cbor:"-2,keyasint,omitempty"` // X for curve x-coordinate, E for RSA public exponent
	Y         RawMessage `cbor:"-3,keyasint,omitempty"` // Y for curve y-cooridate
	D         []byte     `cbor:"-4,keyasint,omitempty"`
}

type attestationObject struct {
	AuthnData []byte     `cbor:"authData"`
	Fmt       string     `cbor:"fmt"`
	AttStmt   RawMessage `cbor:"attStmt"`
}

type SenMLRecord struct {
	BaseName    string  `cbor:"-2,keyasint,omitempty"`
	BaseTime    float64 `cbor:"-3,keyasint,omitempty"`
	BaseUnit    string  `cbor:"-4,keyasint,omitempty"`
	BaseValue   float64 `cbor:"-5,keyasint,omitempty"`
	BaseSum     float64 `cbor:"-6,keyasint,omitempty"`
	BaseVersion int     `cbor:"-1,keyasint,omitempty"`
	Name        string  `cbor:"0,keyasint,omitempty"`
	Unit        string  `cbor:"1,keyasint,omitempty"`
	Time        float64 `cbor:"6,keyasint,omitempty"`
	UpdateTime  float64 `cbor:"7,keyasint,omitempty"`
	Value       float64 `cbor:"2,keyasint,omitempty"`
	ValueS      string  `cbor:"3,keyasint,omitempty"`
	ValueB      bool    `cbor:"4,keyasint,omitempty"`
	ValueD      string  `cbor:"8,keyasint,omitempty"`
	Sum         float64 `cbor:"5,keyasint,omitempty"`
}

type T1 struct {
	T    bool
	UI   uint
	I    int
	F    float64
	B    []byte
	S    string
	Slci []int
	Mss  map[string]string
}

type T2 struct {
	T    bool              `cbor:"1,keyasint"`
	UI   uint              `cbor:"2,keyasint"`
	I    int               `cbor:"3,keyasint"`
	F    float64           `cbor:"4,keyasint"`
	B    []byte            `cbor:"5,keyasint"`
	S    string            `cbor:"6,keyasint"`
	Slci []int             `cbor:"7,keyasint"`
	Mss  map[string]string `cbor:"8,keyasint"`
}

type T3 struct {
	_    struct{} `cbor:",toarray"`
	T    bool
	UI   uint
	I    int
	F    float64
	B    []byte
	S    string
	Slci []int
	Mss  map[string]string
}

type ManyFieldsOneOmitEmpty struct {
	F01, F02, F03, F04, F05, F06, F07, F08, F09, F10, F11, F12, F13, F14, F15, F16 int
	F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31      int

	F32 int `cbor:",omitempty"`
}

type SomeFieldsOneOmitEmpty struct {
	F01, F02, F03, F04, F05, F06, F07 int

	F08 int `cbor:",omitempty"`
}

type ManyFieldsAllOmitEmpty struct {
	F01 int `cbor:",omitempty"`
	F02 int `cbor:",omitempty"`
	F03 int `cbor:",omitempty"`
	F04 int `cbor:",omitempty"`
	F05 int `cbor:",omitempty"`
	F06 int `cbor:",omitempty"`
	F07 int `cbor:",omitempty"`
	F08 int `cbor:",omitempty"`
	F09 int `cbor:",omitempty"`
	F10 int `cbor:",omitempty"`
	F11 int `cbor:",omitempty"`
	F12 int `cbor:",omitempty"`
	F13 int `cbor:",omitempty"`
	F14 int `cbor:",omitempty"`
	F15 int `cbor:",omitempty"`
	F16 int `cbor:",omitempty"`
	F17 int `cbor:",omitempty"`
	F18 int `cbor:",omitempty"`
	F19 int `cbor:",omitempty"`
	F20 int `cbor:",omitempty"`
	F21 int `cbor:",omitempty"`
	F22 int `cbor:",omitempty"`
	F23 int `cbor:",omitempty"`
	F24 int `cbor:",omitempty"`
	F25 int `cbor:",omitempty"`
	F26 int `cbor:",omitempty"`
	F27 int `cbor:",omitempty"`
	F28 int `cbor:",omitempty"`
	F29 int `cbor:",omitempty"`
	F30 int `cbor:",omitempty"`
	F31 int `cbor:",omitempty"`
	F32 int `cbor:",omitempty"`
}

type SomeFieldsAllOmitEmpty struct {
	F01 int `cbor:",omitempty"`
	F02 int `cbor:",omitempty"`
	F03 int `cbor:",omitempty"`
	F04 int `cbor:",omitempty"`
	F05 int `cbor:",omitempty"`
	F06 int `cbor:",omitempty"`
	F07 int `cbor:",omitempty"`
	F08 int `cbor:",omitempty"`
}

var decodeBenchmarks = []struct {
	name          string
	data          []byte
	decodeToTypes []reflect.Type
}{
	{"bool", hexDecode("f5"), []reflect.Type{typeIntf, typeBool}},                                                                                                                                                                                           // true
	{"positive int", hexDecode("1bffffffffffffffff"), []reflect.Type{typeIntf, typeUint64}},                                                                                                                                                                 // uint64(18446744073709551615)
	{"negative int", hexDecode("3903e7"), []reflect.Type{typeIntf, typeInt64}},                                                                                                                                                                              // int64(-1000)
	{"float", hexDecode("fbc010666666666666"), []reflect.Type{typeIntf, typeFloat64}},                                                                                                                                                                       // float64(-4.1)
	{"bytes", hexDecode("581a0102030405060708090a0b0c0d0e0f101112131415161718191a"), []reflect.Type{typeIntf, typeByteSlice}},                                                                                                                               // []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}
	{"bytes indef len", hexDecode("5f410141024103410441054106410741084109410a410b410c410d410e410f4110411141124113411441154116411741184119411aff"), []reflect.Type{typeIntf, typeByteSlice}},                                                                 // []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}
	{"text", hexDecode("782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67"), []reflect.Type{typeIntf, typeString}},                                                                                                 // "The quick brown fox jumps over the lazy dog"
	{"text indef len", hexDecode("7f61546168616561206171617561696163616b612061626172616f6177616e61206166616f61786120616a6175616d617061736120616f61766165617261206174616861656120616c6161617a617961206164616f6167ff"), []reflect.Type{typeIntf, typeString}}, // "The quick brown fox jumps over the lazy dog"
	{"array", hexDecode("981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a"), []reflect.Type{typeIntf, typeIntSlice}},                                                                                                                          // []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}
	{"array indef len", hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819181aff"), []reflect.Type{typeIntf, typeIntSlice}},                                                                                                                // []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}
	{"map", hexDecode("ad616161416162614261636143616461446165614561666146616761476168614861696149616a614a616c614c616d614d616e614e"), []reflect.Type{typeIntf, typeMapStringIntf, typeMapStringString}},                                                      // map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}}
	{"map indef len", hexDecode("bf616161416162614261636143616461446165614561666146616761476168614861696149616a614a616b614b616c614c616d614d616e614eff"), []reflect.Type{typeIntf, typeMapStringIntf, typeMapStringString}},                                  // map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}}
}

var encodeBenchmarks = []struct {
	name   string
	data   []byte
	values []interface{}
}{
	{"bool", hexDecode("f5"), []interface{}{true}},
	{"positive int", hexDecode("1bffffffffffffffff"), []interface{}{uint64(18446744073709551615)}},
	{"negative int", hexDecode("3903e7"), []interface{}{int64(-1000)}},
	{"float", hexDecode("fbc010666666666666"), []interface{}{float64(-4.1)}},
	{"bytes", hexDecode("581a0102030405060708090a0b0c0d0e0f101112131415161718191a"), []interface{}{[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}}},
	{"text", hexDecode("782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67"), []interface{}{"The quick brown fox jumps over the lazy dog"}},
	{"array", hexDecode("981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a"), []interface{}{[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}}},
	{"map", hexDecode("ad616161416162614261636143616461446165614561666146616761476168614861696149616a614a616c614c616d614d616e614e"), []interface{}{map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}}},
}

func BenchmarkUnmarshal(b *testing.B) {
	for _, bm := range decodeBenchmarks {
		for _, t := range bm.decodeToTypes {
			name := "CBOR " + bm.name + " to Go " + t.String()
			if t.Kind() == reflect.Struct {
				name = "CBOR " + bm.name + " to Go " + t.Kind().String()
			}
			b.Run(name, func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					vPtr := reflect.New(t).Interface()
					if err := Unmarshal(bm.data, vPtr); err != nil {
						b.Fatal("Unmarshal:", err)
					}
				}
			})
		}
	}
	var moreBenchmarks = []struct {
		name         string
		data         []byte
		decodeToType reflect.Type
	}{
		// Unmarshal CBOR map with string key to map[string]interface{}.
		{
			"CBOR map to Go map[string]interface{}",
			hexDecode("a86154f56255691bffffffffffffffff61493903e76146fbc0106666666666666142581a0102030405060708090a0b0c0d0e0f101112131415161718191a6153782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6764536c6369981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a634d7373ad6163614361656145616661466167614761686148616e614e616d614d61616141616261426164614461696149616a614a616c614c"),
			reflect.TypeOf(map[string]interface{}{}),
		},
		// Unmarshal CBOR map with string key to struct.
		{
			"CBOR map to Go struct",
			hexDecode("a86154f56255491bffffffffffffffff61493903e76146fbc0106666666666666142581a0102030405060708090a0b0c0d0e0f101112131415161718191a6153782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6764536c6369981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a634d7373ad6163614361656145616661466167614761686148616e614e616d614d61616141616261426164614461696149616a614a616c614c"),
			reflect.TypeOf(T1{}),
		},
		// Unmarshal CBOR map with integer key, such as COSE Key and SenML, to map[int]interface{}.
		{
			"CBOR map to Go map[int]interface{}",
			hexDecode("a801f5021bffffffffffffffff033903e704fbc01066666666666605581a0102030405060708090a0b0c0d0e0f101112131415161718191a06782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6707981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a08ad61646144616661466167614761686148616d614d616e614e6161614161626142616361436165614561696149616a614a616c614c"),
			reflect.TypeOf(map[int]interface{}{}),
		},
		// Unmarshal CBOR map with integer key, such as COSE Key and SenML, to struct.
		{
			"CBOR map to Go struct keyasint",
			hexDecode("a801f5021bffffffffffffffff033903e704fbc01066666666666605581a0102030405060708090a0b0c0d0e0f101112131415161718191a06782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f6707981a0102030405060708090a0b0c0d0e0f101112131415161718181819181a08ad61646144616661466167614761686148616d614d616e614e6161614161626142616361436165614561696149616a614a616c614c"),
			reflect.TypeOf(T2{}),
		},
		// Unmarshal CBOR array of known sequence of data types, such as signed/maced/encrypted CWT, to []interface{}.
		{
			"CBOR array to Go []interface{}",
			hexDecode("88f51bffffffffffffffff3903e7fbc010666666666666581a0102030405060708090a0b0c0d0e0f101112131415161718191a782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67981a0102030405060708090a0b0c0d0e0f101112131415161718181819181aad616261426163614361646144616561456166614661696149616e614e616161416167614761686148616a614a616c614c616d614d"),
			reflect.TypeOf([]interface{}{}),
		},
		// Unmarshal CBOR array of known sequence of data types, such as signed/maced/encrypted CWT, to struct.
		{
			"CBOR array to Go struct toarray",
			hexDecode("88f51bffffffffffffffff3903e7fbc010666666666666581a0102030405060708090a0b0c0d0e0f101112131415161718191a782b54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f67981a0102030405060708090a0b0c0d0e0f101112131415161718181819181aad616261426163614361646144616561456166614661696149616e614e616161416167614761686148616a614a616c614c616d614d"),
			reflect.TypeOf(T3{}),
		},
	}
	for _, bm := range moreBenchmarks {
		b.Run(bm.name, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				vPtr := reflect.New(bm.decodeToType).Interface()
				if err := Unmarshal(bm.data, vPtr); err != nil {
					b.Fatal("Unmarshal:", err)
				}
			}
		})
	}
}

func BenchmarkUnmarshalFirst(b *testing.B) {
	// Random trailing data
	trailingData := hexDecode("4a6b0f4718c73f391091ea1c")
	for _, bm := range decodeBenchmarks {
		for _, t := range bm.decodeToTypes {
			name := "CBOR " + bm.name + " to Go " + t.String()
			if t.Kind() == reflect.Struct {
				name = "CBOR " + bm.name + " to Go " + t.Kind().String()
			}
			data := make([]byte, 0, len(bm.data)+len(trailingData))
			data = append(data, bm.data...)
			data = append(data, trailingData...)
			b.Run(name, func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					vPtr := reflect.New(t).Interface()
					if _, err := UnmarshalFirst(data, vPtr); err != nil {
						b.Fatal("UnmarshalFirst:", err)
					}
				}
			})
		}
	}
}

func BenchmarkUnmarshalFirstViaDecoder(b *testing.B) {
	// Random trailing data
	trailingData := hexDecode("4a6b0f4718c73f391091ea1c")
	for _, bm := range decodeBenchmarks {
		for _, t := range bm.decodeToTypes {
			name := "CBOR " + bm.name + " to Go " + t.String()
			if t.Kind() == reflect.Struct {
				name = "CBOR " + bm.name + " to Go " + t.Kind().String()
			}
			data := make([]byte, 0, len(bm.data)+len(trailingData))
			data = append(data, bm.data...)
			data = append(data, trailingData...)
			b.Run(name, func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					vPtr := reflect.New(t).Interface()
					if err := NewDecoder(bytes.NewReader(data)).Decode(vPtr); err != nil {
						b.Fatal("UnmarshalDecoder:", err)
					}
				}
			})
		}
	}
}

func BenchmarkDecode(b *testing.B) {
	for _, bm := range decodeBenchmarks {
		for _, t := range bm.decodeToTypes {
			name := "CBOR " + bm.name + " to Go " + t.String()
			if t.Kind() == reflect.Struct {
				name = "CBOR " + bm.name + " to Go " + t.Kind().String()
			}
			buf := bytes.NewReader(bm.data)
			decoder := NewDecoder(buf)
			b.Run(name, func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					vPtr := reflect.New(t).Interface()
					if err := decoder.Decode(vPtr); err != nil {
						b.Fatal("Decode:", err)
					}
					buf.Seek(0, 0) //nolint:errcheck
				}
			})
		}
	}
}

func BenchmarkDecodeStream(b *testing.B) {
	var data []byte
	for _, bm := range decodeBenchmarks {
		for i := 0; i < len(bm.decodeToTypes); i++ {
			data = append(data, bm.data...)
		}
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		buf := bytes.NewReader(data)
		decoder := NewDecoder(buf)
		for j := 0; j < rounds; j++ {
			for _, bm := range decodeBenchmarks {
				for _, t := range bm.decodeToTypes {
					vPtr := reflect.New(t).Interface()
					if err := decoder.Decode(vPtr); err != nil {
						b.Fatal("Decode:", err)
					}
				}
			}
			buf.Seek(0, 0) //nolint:errcheck
		}
	}
}

func BenchmarkMarshal(b *testing.B) {
	for _, bm := range encodeBenchmarks {
		for _, v := range bm.values {
			name := "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name
			if reflect.TypeOf(v).Kind() == reflect.Struct {
				name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name
			}
			b.Run(name, func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					if _, err := Marshal(v); err != nil {
						b.Fatal("Marshal:", err)
					}
				}
			})
		}
	}
	// Marshal map[string]interface{} to CBOR map
	m1 := map[string]interface{}{ //nolint:dupl
		"T":    true,
		"UI":   uint(18446744073709551615),
		"I":    -1000,
		"F":    -4.1,
		"B":    []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		"S":    "The quick brown fox jumps over the lazy dog",
		"Slci": []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		"Mss":  map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"},
	}
	// Marshal struct to CBOR map
	v1 := T1{ //nolint:dupl
		T:    true,
		UI:   18446744073709551615,
		I:    -1000,
		F:    -4.1,
		B:    []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		S:    "The quick brown fox jumps over the lazy dog",
		Slci: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		Mss:  map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"},
	}
	// Marshal map[int]interface{} to CBOR map
	m2 := map[int]interface{}{ //nolint:dupl
		1: true,
		2: uint(18446744073709551615),
		3: -1000,
		4: -4.1,
		5: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		6: "The quick brown fox jumps over the lazy dog",
		7: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		8: map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"},
	}
	// Marshal struct keyasint, such as COSE Key and SenML
	v2 := T2{ //nolint:dupl
		T:    true,
		UI:   18446744073709551615,
		I:    -1000,
		F:    -4.1,
		B:    []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		S:    "The quick brown fox jumps over the lazy dog",
		Slci: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		Mss:  map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"},
	}
	// Marshal []interface to CBOR array.
	slc := []interface{}{
		true,
		uint(18446744073709551615),
		-1000,
		-4.1,
		[]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		"The quick brown fox jumps over the lazy dog",
		[]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"},
	}
	// Marshal struct toarray to CBOR array, such as signed/maced/encrypted CWT.
	v3 := T3{ //nolint:dupl
		T:    true,
		UI:   18446744073709551615,
		I:    -1000,
		F:    -4.1,
		B:    []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		S:    "The quick brown fox jumps over the lazy dog",
		Slci: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
		Mss:  map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"},
	}
	var moreBenchmarks = []struct {
		name  string
		value interface{}
	}{
		{"Go map[string]interface{} to CBOR map", m1},
		{"Go struct to CBOR map", v1},
		{"Go struct many fields all omitempty all empty to CBOR map", ManyFieldsAllOmitEmpty{}},
		{"Go struct some fields all omitempty all empty to CBOR map", SomeFieldsAllOmitEmpty{}},
		{"Go struct many fields all omitempty all nonempty to CBOR map", ManyFieldsAllOmitEmpty{
			F01: 1, F02: 1, F03: 1, F04: 1, F05: 1, F06: 1, F07: 1, F08: 1, F09: 1, F10: 1, F11: 1, F12: 1, F13: 1, F14: 1, F15: 1, F16: 1,
			F17: 1, F18: 1, F19: 1, F20: 1, F21: 1, F22: 1, F23: 1, F24: 1, F25: 1, F26: 1, F27: 1, F28: 1, F29: 1, F30: 1, F31: 1, F32: 1,
		}},
		{"Go struct some fields all omitempty all nonempty to CBOR map", SomeFieldsAllOmitEmpty{
			F01: 1, F02: 1, F03: 1, F04: 1, F05: 1, F06: 1, F07: 1, F08: 1,
		}},
		{"Go struct many fields one omitempty to CBOR map", ManyFieldsOneOmitEmpty{}},
		{"Go struct some fields one omitempty to CBOR map", SomeFieldsOneOmitEmpty{}},
		{"Go map[int]interface{} to CBOR map", m2},
		{"Go struct keyasint to CBOR map", v2},
		{"Go []interface{} to CBOR map", slc},
		{"Go struct toarray to CBOR array", v3},
	}
	for _, bm := range moreBenchmarks {
		b.Run(bm.name, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				if _, err := Marshal(bm.value); err != nil {
					b.Fatal("Marshal:", err)
				}
			}
		})
	}
}

func BenchmarkMarshalCanonical(b *testing.B) {
	type strc struct {
		A string `cbor:"a"`
		B string `cbor:"b"`
		C string `cbor:"c"`
		D string `cbor:"d"`
		E string `cbor:"e"`
		F string `cbor:"f"`
		G string `cbor:"g"`
		H string `cbor:"h"`
		I string `cbor:"i"`
		J string `cbor:"j"`
		L string `cbor:"l"`
		M string `cbor:"m"`
		N string `cbor:"n"`
	}
	for _, bm := range []struct {
		name   string
		data   []byte
		values []interface{}
	}{
		{"map", hexDecode("ad616161416162614261636143616461446165614561666146616761476168614861696149616a614a616c614c616d614d616e614e"), []interface{}{map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "f": "F", "g": "G", "h": "H", "i": "I", "j": "J", "l": "L", "m": "M", "n": "N"}, strc{A: "A", B: "B", C: "C", D: "D", E: "E", F: "F", G: "G", H: "H", I: "I", J: "J", L: "L", M: "M", N: "N"}, map[int]int{0: 0} /* single-entry map */}},
	} {
		for _, v := range bm.values {
			name := "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name
			if reflect.TypeOf(v).Kind() == reflect.Struct {
				name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name
			}
			b.Run(name, func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					if _, err := Marshal(v); err != nil {
						b.Fatal("Marshal:", err)
					}
				}
			})
			// Canonical encoding
			name = "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name + " canonical"
			if reflect.TypeOf(v).Kind() == reflect.Struct {
				name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name + " canonical"
			}
			em, _ := EncOptions{Sort: SortCanonical}.EncMode()
			b.Run(name, func(b *testing.B) {
				for i := 0; i < b.N; i++ {
					if _, err := em.Marshal(v); err != nil {
						b.Fatal("Marshal:", err)
					}
				}
			})
		}
	}
}

// BenchmarkNewEncoderEncode benchmarks NewEncoder() and Encode().
func BenchmarkNewEncoderEncode(b *testing.B) {
	for _, bm := range encodeBenchmarks {
		for _, v := range bm.values {
			name := "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name
			if reflect.TypeOf(v).Kind() == reflect.Struct {
				name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name
			}
			b.Run(name, func(b *testing.B) {
				b.ResetTimer()
				for i := 0; i < b.N; i++ {
					encoder := NewEncoder(io.Discard)
					if err := encoder.Encode(v); err != nil {
						b.Fatal("Encode:", err)
					}
				}
			})
		}
	}
}

// BenchmarkEncode benchmarks Encode(). It reuses same Encoder to exclude NewEncoder()
// from the benchmark.
func BenchmarkEncode(b *testing.B) {
	for _, bm := range encodeBenchmarks {
		for _, v := range bm.values {
			name := "Go " + reflect.TypeOf(v).String() + " to CBOR " + bm.name
			if reflect.TypeOf(v).Kind() == reflect.Struct {
				name = "Go " + reflect.TypeOf(v).Kind().String() + " to CBOR " + bm.name
			}
			b.Run(name, func(b *testing.B) {
				encoder := NewEncoder(io.Discard)
				b.ResetTimer()
				for i := 0; i < b.N; i++ {
					if err := encoder.Encode(v); err != nil {
						b.Fatal("Encode:", err)
					}
				}
			})
		}
	}
}

func BenchmarkEncodeStream(b *testing.B) {
	for i := 0; i < b.N; i++ {
		encoder := NewEncoder(io.Discard)
		for i := 0; i < rounds; i++ {
			for _, bm := range encodeBenchmarks {
				for _, v := range bm.values {
					if err := encoder.Encode(v); err != nil {
						b.Fatal("Encode:", err)
					}
				}
			}
		}
	}
}

func BenchmarkUnmarshalCOSE(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2
	testCases := []struct {
		name string
		data []byte
	}{
		{"128-Bit Symmetric Key", hexDecode("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")},
		{"256-Bit Symmetric Key", hexDecode("a4205820403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d795693880104024c53796d6d6574726963323536030a")},
		{"ECDSA P256 256-Bit Key", hexDecode("a72358206c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c1922582060f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db9529971a36e7b9215820143329cce7868e416927599cf65a34f3ce2ffda55a7eca69ed8919a394d42f0f2001010202524173796d6d657472696345434453413235360326")},
	}
	for _, tc := range testCases {
		b.Run(tc.name, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				var v coseKey
				if err := Unmarshal(tc.data, &v); err != nil {
					b.Fatal("Unmarshal:", err)
				}
			}
		})
	}
}

func BenchmarkMarshalCOSE(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.2
	testCases := []struct {
		name string
		data []byte
	}{
		{"128-Bit Symmetric Key", hexDecode("a42050231f4c4d4d3051fdc2ec0a3851d5b3830104024c53796d6d6574726963313238030a")},
		{"256-Bit Symmetric Key", hexDecode("a4205820403697de87af64611c1d32a05dab0fe1fcb715a86ab435f1ec99192d795693880104024c53796d6d6574726963323536030a")},
		{"ECDSA P256 256-Bit Key", hexDecode("a72358206c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c1922582060f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db9529971a36e7b9215820143329cce7868e416927599cf65a34f3ce2ffda55a7eca69ed8919a394d42f0f2001010202524173796d6d657472696345434453413235360326")},
	}
	for _, tc := range testCases {
		var v coseKey
		if err := Unmarshal(tc.data, &v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
		b.Run(tc.name, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				if _, err := Marshal(v); err != nil {
					b.Fatal("Marshal:", err)
				}
			}
		})
	}
}

func BenchmarkUnmarshalCWTClaims(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
	data := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
	for i := 0; i < b.N; i++ {
		var v claims
		if err := Unmarshal(data, &v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkUnmarshalCWTClaimsWithDupMapKeyOpt(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
	data := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
	for i := 0; i < b.N; i++ {
		var v claims
		if err := dm.Unmarshal(data, &v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkMarshalCWTClaims(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
	data := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
	var v claims
	if err := Unmarshal(data, &v); err != nil {
		b.Fatal("Unmarshal:", err)
	}
	for i := 0; i < b.N; i++ {
		if _, err := Marshal(v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkUnmarshalSenML(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8428#section-6
	data := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
	for i := 0; i < b.N; i++ {
		var v []SenMLRecord
		if err := Unmarshal(data, &v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkMarshalSenML(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8428#section-6
	data := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
	var v []SenMLRecord
	if err := Unmarshal(data, &v); err != nil {
		b.Fatal("Unmarshal:", err)
	}
	for i := 0; i < b.N; i++ {
		if _, err := Marshal(v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkMarshalSenMLShortestFloat16(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8428#section-6
	data := hexDecode("87a721781b75726e3a6465763a6f773a3130653230373361303130383030363a22fb41d303a15b00106223614120050067766f6c7461676501615602fb405e066666666666a3006763757272656e74062402fb3ff3333333333333a3006763757272656e74062302fb3ff4cccccccccccda3006763757272656e74062202fb3ff6666666666666a3006763757272656e74062102f93e00a3006763757272656e74062002fb3ff999999999999aa3006763757272656e74060002fb3ffb333333333333")
	var v []SenMLRecord
	if err := Unmarshal(data, &v); err != nil {
		b.Fatal("Unmarshal:", err)
	}
	em, _ := EncOptions{ShortestFloat: ShortestFloat16}.EncMode()
	for i := 0; i < b.N; i++ {
		if _, err := em.Marshal(v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkUnmarshalWebAuthn(b *testing.B) {
	// Data generated from Yubico security key
	data := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
	for i := 0; i < b.N; i++ {
		var v attestationObject
		if err := Unmarshal(data, &v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkMarshalWebAuthn(b *testing.B) {
	// Data generated from Yubico security key
	data := hexDecode("a363666d74686669646f2d7532666761747453746d74a26373696758483046022100e7ab373cfbd99fcd55fd59b0f6f17fef5b77a20ddec3db7f7e4d55174e366236022100828336b4822125fb56541fb14a8a273876acd339395ec2dad95cf41c1dd2a9ae637835638159024e3082024a30820132a0030201020204124a72fe300d06092a864886f70d01010b0500302e312c302a0603550403132359756269636f2055324620526f6f742043412053657269616c203435373230303633313020170d3134303830313030303030305a180f32303530303930343030303030305a302c312a302806035504030c2159756269636f205532462045452053657269616c203234393431343937323135383059301306072a8648ce3d020106082a8648ce3d030107034200043d8b1bbd2fcbf6086e107471601468484153c1c6d3b4b68a5e855e6e40757ee22bcd8988bf3befd7cdf21cb0bf5d7a150d844afe98103c6c6607d9faae287c02a33b3039302206092b0601040182c40a020415312e332e362e312e342e312e34313438322e312e313013060b2b0601040182e51c020101040403020520300d06092a864886f70d01010b05000382010100a14f1eea0076f6b8476a10a2be72e60d0271bb465b2dfbfc7c1bd12d351989917032631d795d097fa30a26a325634e85721bc2d01a86303f6bc075e5997319e122148b0496eec8d1f4f94cf4110de626c289443d1f0f5bbb239ca13e81d1d5aa9df5af8e36126475bfc23af06283157252762ff68879bcf0ef578d55d67f951b4f32b63c8aea5b0f99c67d7d814a7ff5a6f52df83e894a3a5d9c8b82e7f8bc8daf4c80175ff8972fda79333ec465d806eacc948f1bab22045a95558a48c20226dac003d41fbc9e05ea28a6bb5e10a49de060a0a4f6a2676a34d68c4abe8c61874355b9027e828ca9e064b002d62e8d8cf0744921753d35e3c87c5d5779453e7768617574684461746158c449960de5880e8c687434170f6476605b8fe4aeb9a28632c7995cf3ba831d976341000000000000000000000000000000000000000000408903fd7dfd2c9770e98cae0123b13a2c27828a106349bc6277140e7290b7e9eb7976aa3c04ed347027caf7da3a2fa76304751c02208acfc4e7fc6c7ebbc375c8a5010203262001215820ad7f7992c335b90d882b2802061b97a4fabca7e2ee3e7a51e728b8055e4eb9c7225820e0966ba7005987fece6f0e0e13447aa98cec248e4000a594b01b74c1cb1d40b3")
	var v attestationObject
	if err := Unmarshal(data, &v); err != nil {
		b.Fatal("Unmarshal:", err)
	}
	for i := 0; i < b.N; i++ {
		if _, err := Marshal(v); err != nil {
			b.Fatal("Marshal:", err)
		}
	}
}

func BenchmarkUnmarshalCOSEMAC(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
	data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")

	for i := 0; i < b.N; i++ {
		var v macedCOSE
		if err := Unmarshal(data, &v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}

func BenchmarkUnmarshalCOSEMACWithTag(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
	data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")

	// Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type
	tags := NewTagSet()
	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(macedCOSE{}), 61, 17); err != nil {
		b.Fatal("TagSet.Add:", err)
	}

	// Create DecMode with tags
	dm, _ := DecOptions{}.DecModeWithTags(tags)

	for i := 0; i < b.N; i++ {
		var v macedCOSE
		if err := dm.Unmarshal(data, &v); err != nil {
			b.Fatal("Unmarshal:", err)
		}
	}
}
func BenchmarkMarshalCOSEMAC(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
	data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")

	var v macedCOSE
	if err := Unmarshal(data, &v); err != nil {
		b.Fatal("Unmarshal():", err)
	}

	for i := 0; i < b.N; i++ {
		if _, err := Marshal(v); err != nil {
			b.Fatal("Marshal():", v, err)
		}
	}
}

func BenchmarkMarshalCOSEMACWithTag(b *testing.B) {
	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
	data := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")

	// Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type
	tags := NewTagSet()
	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, reflect.TypeOf(macedCOSE{}), 61, 17); err != nil {
		b.Fatal("TagSet.Add:", err)
	}

	// Create EncMode with tags.
	dm, _ := DecOptions{}.DecModeWithTags(tags)
	em, _ := EncOptions{}.EncModeWithTags(tags)

	var v macedCOSE
	if err := dm.Unmarshal(data, &v); err != nil {
		b.Fatal("Unmarshal():", err)
	}

	for i := 0; i < b.N; i++ {
		if _, err := em.Marshal(v); err != nil {
			b.Fatal("Marshal():", v, err)
		}
	}
}

func BenchmarkUnmarshalMapToStruct(b *testing.B) {
	type S struct {
		A, B, C, D, E, F, G, H, I, J, K, L, M bool
	}

	var (
		allKnownFields            = hexDecode("ad6141f56142f56143f56144f56145f56146f56147f56148f56149f5614af5614bf5614cf5614df5") // {"A": true, ... "M": true }
		allKnownDuplicateFields   = hexDecode("ad6141f56141f56141f56141f56141f56141f56141f56141f56141f56141f56141f56141f56141f5") // {"A": true, "A": true, "A": true, ...}
		allUnknownFields          = hexDecode("ad614ef5614ff56150f56151f56152f56153f56154f56155f56156f56157f56158f56159f5615af5") // {"N": true, ... "Z": true }
		allUnknownDuplicateFields = hexDecode("ad614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5614ef5") // {"N": true, "N": true, "N": true, ...}
	)

	type ManyFields struct {
		AA, AB, AC, AD, AE, AF, AG, AH, AI, AJ, AK, AL, AM, AN, AO, AP, AQ, AR, AS, AT, AU, AV, AW, AX, AY, AZ bool
		BA, BB, BC, BD, BE, BF, BG, BH, BI, BJ, BK, BL, BM, BN, BO, BP, BQ, BR, BS, BT, BU, BV, BW, BX, BY, BZ bool
		CA, CB, CC, CD, CE, CF, CG, CH, CI, CJ, CK, CL, CM, CN, CO, CP, CQ, CR, CS, CT, CU, CV, CW, CX, CY, CZ bool
		DA, DB, DC, DD, DE, DF, DG, DH, DI, DJ, DK, DL, DM, DN, DO, DP, DQ, DR, DS, DT, DU, DV, DW, DX, DY, DZ bool
	}
	var manyFieldsOneKeyPerField []byte
	{
		// An EncOption that accepts a function to sort or shuffle keys might be useful for
		// cases like this. Here we are manually encoding the fields in reverse order to
		// target worst-case key-to-field matching.
		rt := reflect.TypeOf(ManyFields{})
		var buf bytes.Buffer
		if rt.NumField() > 255 {
			b.Fatalf("invalid test assumption: ManyFields expected to have no more than 255 fields, has %d", rt.NumField())
		}
		buf.WriteByte(0xb8)
		buf.WriteByte(byte(rt.NumField()))
		for i := rt.NumField() - 1; i >= 0; i-- { // backwards
			f := rt.Field(i)
			if len(f.Name) > 23 {
				b.Fatalf("invalid test assumption: field name %q longer than 23 bytes", f.Name)
			}
			buf.WriteByte(byte(0x60 + len(f.Name)))
			buf.WriteString(f.Name)
			buf.WriteByte(0xf5) // true
		}
		manyFieldsOneKeyPerField = buf.Bytes()
	}

	type input struct {
		name   string
		data   []byte
		into   interface{}
		reject bool
	}

	for _, tc := range []*struct {
		name   string
		opts   DecOptions
		inputs []input
	}{
		{
			name: "default options",
			opts: DecOptions{},
			inputs: []input{
				{
					name:   "all known fields",
					data:   allKnownFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all known duplicate fields",
					data:   allKnownDuplicateFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all unknown fields",
					data:   allUnknownFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all unknown duplicate fields",
					data:   allUnknownDuplicateFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "many fields one key per field",
					data:   manyFieldsOneKeyPerField,
					into:   ManyFields{},
					reject: false,
				},
			},
		},
		{
			name: "reject unknown",
			opts: DecOptions{ExtraReturnErrors: ExtraDecErrorUnknownField},
			inputs: []input{
				{
					name:   "all known fields",
					data:   allKnownFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all known duplicate fields",
					data:   allKnownDuplicateFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all unknown fields",
					data:   allUnknownFields,
					into:   S{},
					reject: true,
				},
				{
					name:   "all unknown duplicate fields",
					data:   allUnknownDuplicateFields,
					into:   S{},
					reject: true,
				},
			},
		},
		{
			name: "reject duplicate",
			opts: DecOptions{DupMapKey: DupMapKeyEnforcedAPF},
			inputs: []input{
				{
					name:   "all known fields",
					data:   allKnownFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all known duplicate fields",
					data:   allKnownDuplicateFields,
					into:   S{},
					reject: true,
				},
				{
					name:   "all unknown fields",
					data:   allUnknownFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all unknown duplicate fields",
					data:   allUnknownDuplicateFields,
					into:   S{},
					reject: true,
				},
			},
		},
		{
			name: "reject unknown and duplicate",
			opts: DecOptions{
				DupMapKey:         DupMapKeyEnforcedAPF,
				ExtraReturnErrors: ExtraDecErrorUnknownField,
			},
			inputs: []input{
				{
					name:   "all known fields",
					data:   allKnownFields,
					into:   S{},
					reject: false,
				},
				{
					name:   "all known duplicate fields",
					data:   allKnownDuplicateFields,
					into:   S{},
					reject: true,
				},
				{
					name:   "all unknown fields",
					data:   allUnknownFields,
					into:   S{},
					reject: true,
				},
				{
					name:   "all unknown duplicate fields",
					data:   allUnknownDuplicateFields,
					into:   S{},
					reject: true,
				},
			},
		},
	} {
		for _, in := range tc.inputs {
			b.Run(fmt.Sprintf("%s/%s", tc.name, in.name), func(b *testing.B) {
				dm, err := tc.opts.DecMode()
				if err != nil {
					b.Fatal(err)
				}

				dst := reflect.New(reflect.TypeOf(in.into)).Interface()

				b.ResetTimer()
				for i := 0; i < b.N; i++ {
					if err := dm.Unmarshal(in.data, dst); !in.reject && err != nil {
						b.Fatalf("unexpected error: %v", err)
					} else if in.reject && err == nil {
						b.Fatal("expected non-nil error")
					}
				}
			})
		}
	}
}
