Repository: fxamacker/cbor
Pull request #542 (2024-05-27 21:13:12)

Refactor and improve code
This PR refactors and improves code.


Patch:
diff --git a/cache.go b/cache.go
index 8b4d463..ea0f39e 100644
--- a/cache.go
+++ b/cache.go
@@ -276,7 +276,8 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
 			copy(flds[i].cborNameByteString, flds[i].cborName)
 			// Reset encoded CBOR type to byte string, preserving the "additional
 			// information" bits:
-			flds[i].cborNameByteString[0] = byte(cborTypeByteString) | (flds[i].cborNameByteString[0] & 0x1f)
+			flds[i].cborNameByteString[0] = byte(cborTypeByteString) |
+				getAdditionalInformation(flds[i].cborNameByteString[0])
 
 			hasKeyAsStr = true
 		}
diff --git a/common.go b/common.go
new file mode 100644
index 0000000..0387db6
--- /dev/null
+++ b/common.go
@@ -0,0 +1,183 @@
+// Copyright (c) Faye Amacker. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+package cbor
+
+import (
+	"fmt"
+	"strconv"
+)
+
+type cborType uint8
+
+const (
+	cborTypePositiveInt cborType = 0x00
+	cborTypeNegativeInt cborType = 0x20
+	cborTypeByteString  cborType = 0x40
+	cborTypeTextString  cborType = 0x60
+	cborTypeArray       cborType = 0x80
+	cborTypeMap         cborType = 0xa0
+	cborTypeTag         cborType = 0xc0
+	cborTypePrimitives  cborType = 0xe0
+)
+
+func (t cborType) String() string {
+	switch t {
+	case cborTypePositiveInt:
+		return "positive integer"
+	case cborTypeNegativeInt:
+		return "negative integer"
+	case cborTypeByteString:
+		return "byte string"
+	case cborTypeTextString:
+		return "UTF-8 text string"
+	case cborTypeArray:
+		return "array"
+	case cborTypeMap:
+		return "map"
+	case cborTypeTag:
+		return "tag"
+	case cborTypePrimitives:
+		return "primitives"
+	default:
+		return "Invalid type " + strconv.Itoa(int(t))
+	}
+}
+
+type additionalInformation uint8
+
+const (
+	maxAdditionalInformationWithoutArgument = 23
+	additionalInformationWith1ByteArgument  = 24
+	additionalInformationWith2ByteArgument  = 25
+	additionalInformationWith4ByteArgument  = 26
+	additionalInformationWith8ByteArgument  = 27
+
+	// For major type 7.
+	additionalInformationAsFalse     = 20
+	additionalInformationAsTrue      = 21
+	additionalInformationAsNull      = 22
+	additionalInformationAsUndefined = 23
+	additionalInformationAsFloat16   = 25
+	additionalInformationAsFloat32   = 26
+	additionalInformationAsFloat64   = 27
+
+	// For major type 2, 3, 4, 5.
+	additionalInformationAsIndefiniteLengthFlag = 31
+)
+
+const (
+	maxSimpleValueInAdditionalInformation = 23
+	minSimpleValueIn1ByteArgument         = 32
+)
+
+func (ai additionalInformation) isIndefiniteLength() bool {
+	return ai == additionalInformationAsIndefiniteLengthFlag
+}
+
+const (
+	// From RFC 8949 Section 3:
+	//   "The initial byte of each encoded data item contains both information about the major type
+	//   (the high-order 3 bits, described in Section 3.1) and additional information
+	//   (the low-order 5 bits)."
+
+	// typeMask is used to extract major type in initial byte of encoded data item.
+	typeMask = 0xe0
+
+	// additionalInformationMask is used to extract additional information in initial byte of encoded data item.
+	additionalInformationMask = 0x1f
+)
+
+func getType(raw byte) cborType {
+	return cborType(raw & typeMask)
+}
+
+func getAdditionalInformation(raw byte) byte {
+	return raw & additionalInformationMask
+}
+
+func isBreakFlag(raw byte) bool {
+	return raw == cborBreakFlag
+}
+
+func parseInitialByte(b byte) (t cborType, ai byte) {
+	return getType(b), getAdditionalInformation(b)
+}
+
+const (
+	tagNumRFC3339Time                    = 0
+	tagNumEpochTime                      = 1
+	tagNumUnsignedBignum                 = 2
+	tagNumNegativeBignum                 = 3
+	tagNumExpectedLaterEncodingBase64URL = 21
+	tagNumExpectedLaterEncodingBase64    = 22
+	tagNumExpectedLaterEncodingBase16    = 23
+	tagNumSelfDescribedCBOR              = 55799
+)
+
+const (
+	cborBreakFlag                          = byte(0xff)
+	cborByteStringWithIndefiniteLengthHead = byte(0x5f)
+	cborTextStringWithIndefiniteLengthHead = byte(0x7f)
+	cborArrayWithIndefiniteLengthHead      = byte(0x9f)
+	cborMapWithIndefiniteLengthHead        = byte(0xbf)
+)
+
+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}
+)
+
+// validBuiltinTag checks that supported built-in tag numbers are followed by expected content types.
+func validBuiltinTag(tagNum uint64, contentHead byte) error {
+	t := getType(contentHead)
+	switch tagNum {
+	case tagNumRFC3339Time:
+		// Tag content (date/time text string in RFC 3339 format) must be string type.
+		if t != cborTypeTextString {
+			return fmt.Errorf(
+				"cbor: tag number %d must be followed by text string, got %s",
+				tagNumRFC3339Time,
+				t.String(),
+			)
+		}
+		return nil
+
+	case tagNumEpochTime:
+		// Tag content (epoch date/time) must be uint, int, or float type.
+		if t != cborTypePositiveInt && t != cborTypeNegativeInt && (contentHead < 0xf9 || contentHead > 0xfb) {
+			return fmt.Errorf(
+				"cbor: tag number %d must be followed by integer or floating-point number, got %s",
+				tagNumEpochTime,
+				t.String(),
+			)
+		}
+		return nil
+
+	case tagNumUnsignedBignum, tagNumNegativeBignum:
+		// Tag content (bignum) must be byte type.
+		if t != cborTypeByteString {
+			return fmt.Errorf(
+				"cbor: tag number %d or %d must be followed by byte string, got %s",
+				tagNumUnsignedBignum,
+				tagNumNegativeBignum,
+				t.String(),
+			)
+		}
+		return nil
+
+	case tagNumExpectedLaterEncodingBase64URL, tagNumExpectedLaterEncodingBase64, tagNumExpectedLaterEncodingBase16:
+		// From RFC 8949 3.4.5.2:
+		//   The data item tagged can be a byte string or any other data item. In the latter
+		//   case, the tag applies to all of the byte string data items contained in the data
+		//   item, except for those contained in a nested data item tagged with an expected
+		//   conversion.
+		return nil
+	}
+
+	return nil
+}
diff --git a/decode.go b/decode.go
index 66599e1..5449d53 100644
--- a/decode.go
+++ b/decode.go
@@ -802,13 +802,13 @@ type DecOptions struct {
 }
 
 // DecMode returns DecMode with immutable options and no tags (safe for concurrency).
-func (opts DecOptions) DecMode() (DecMode, error) {
+func (opts DecOptions) DecMode() (DecMode, error) { //nolint:gocritic // ignore hugeParam
 	return opts.decMode()
 }
 
 // validForTags checks that the provided tag set is compatible with these options and returns a
 // non-nil error if and only if the provided tag set is incompatible.
-func (opts DecOptions) validForTags(tags TagSet) error {
+func (opts DecOptions) validForTags(tags TagSet) error { //nolint:gocritic // ignore hugeParam
 	if opts.TagsMd == TagsForbidden {
 		return errors.New("cbor: cannot create DecMode with TagSet when TagsMd is TagsForbidden")
 	}
@@ -817,9 +817,9 @@ func (opts DecOptions) validForTags(tags TagSet) error {
 	}
 	if opts.ByteStringToString == ByteStringToStringAllowedWithExpectedLaterEncoding || opts.ByteSliceExpectedEncoding != ByteSliceExpectedEncodingIgnored {
 		for _, tagNum := range []uint64{
-			expectedLaterEncodingBase64URLTagNum,
-			expectedLaterEncodingBase64TagNum,
-			expectedLaterEncodingBase16TagNum,
+			tagNumExpectedLaterEncodingBase64URL,
+			tagNumExpectedLaterEncodingBase64,
+			tagNumExpectedLaterEncodingBase16,
 		} {
 			if rt := tags.getTypeFromTagNum([]uint64{tagNum}); rt != nil {
 				return fmt.Errorf("cbor: DecMode with non-default StringExpectedEncoding or ByteSliceExpectedEncoding treats tag %d as built-in and conflicts with the provided TagSet's registration of %v", tagNum, rt)
@@ -831,7 +831,7 @@ func (opts DecOptions) validForTags(tags TagSet) error {
 }
 
 // DecModeWithTags returns DecMode with options and tags that are both immutable (safe for concurrency).
-func (opts DecOptions) DecModeWithTags(tags TagSet) (DecMode, error) {
+func (opts DecOptions) DecModeWithTags(tags TagSet) (DecMode, error) { //nolint:gocritic // ignore hugeParam
 	if err := opts.validForTags(tags); err != nil {
 		return nil, err
 	}
@@ -859,7 +859,7 @@ func (opts DecOptions) DecModeWithTags(tags TagSet) (DecMode, error) {
 }
 
 // DecModeWithSharedTags returns DecMode with immutable options and mutable shared tags (safe for concurrency).
-func (opts DecOptions) DecModeWithSharedTags(tags TagSet) (DecMode, error) {
+func (opts DecOptions) DecModeWithSharedTags(tags TagSet) (DecMode, error) { //nolint:gocritic // ignore hugeParam
 	if err := opts.validForTags(tags); err != nil {
 		return nil, err
 	}
@@ -894,7 +894,7 @@ var defaultSimpleValues = func() *SimpleValueRegistry {
 }()
 
 //nolint:gocyclo // Each option comes with some manageable boilerplate
-func (opts DecOptions) decMode() (*decMode, error) {
+func (opts DecOptions) decMode() (*decMode, error) { //nolint:gocritic // ignore hugeParam
 	if !opts.DupMapKey.valid() {
 		return nil, errors.New("cbor: invalid DupMapKey " + strconv.Itoa(int(opts.DupMapKey)))
 	}
@@ -1275,49 +1275,6 @@ func (d *decoder) value(v interface{}) error {
 	return d.parseToValue(rv, getTypeInfo(rv.Type()))
 }
 
-type cborType uint8
-
-const (
-	cborTypePositiveInt cborType = 0x00
-	cborTypeNegativeInt cborType = 0x20
-	cborTypeByteString  cborType = 0x40
-	cborTypeTextString  cborType = 0x60
-	cborTypeArray       cborType = 0x80
-	cborTypeMap         cborType = 0xa0
-	cborTypeTag         cborType = 0xc0
-	cborTypePrimitives  cborType = 0xe0
-)
-
-func (t cborType) String() string {
-	switch t {
-	case cborTypePositiveInt:
-		return "positive integer"
-	case cborTypeNegativeInt:
-		return "negative integer"
-	case cborTypeByteString:
-		return "byte string"
-	case cborTypeTextString:
-		return "UTF-8 text string"
-	case cborTypeArray:
-		return "array"
-	case cborTypeMap:
-		return "map"
-	case cborTypeTag:
-		return "tag"
-	case cborTypePrimitives:
-		return "primitives"
-	default:
-		return "Invalid type " + strconv.Itoa(int(t))
-	}
-}
-
-const (
-	selfDescribedCBORTagNum              = 55799
-	expectedLaterEncodingBase64URLTagNum = 21
-	expectedLaterEncodingBase64TagNum    = 22
-	expectedLaterEncodingBase16TagNum    = 23
-)
-
 // parseToValue decodes CBOR data to value.  It assumes data is well-formed,
 // and does not perform bounds checking.
 func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo
@@ -1334,7 +1291,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 			// Use value type
 			v = v.Elem()
 			tInfo = getTypeInfo(v.Type())
-		} else {
+		} else { //nolint:gocritic
 			// Create and use registered type if CBOR data is registered tag
 			if d.dm.tags != nil && d.nextCBORType() == cborTypeTag {
 
@@ -1376,7 +1333,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 	for d.nextCBORType() == cborTypeTag {
 		off := d.off
 		_, _, tagNum := d.getHead()
-		if tagNum != selfDescribedCBORTagNum {
+		if tagNum != tagNumSelfDescribedCBOR {
 			d.off = off
 			break
 		}
@@ -1401,8 +1358,10 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 				v.Set(reflect.ValueOf(iv))
 			}
 			return err
+
 		case specialTypeTag:
 			return d.parseToTag(v)
+
 		case specialTypeTime:
 			if d.nextCBORNil() {
 				// Decoding CBOR null and undefined to time.Time is no-op.
@@ -1417,6 +1376,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 				v.Set(reflect.ValueOf(tm))
 			}
 			return nil
+
 		case specialTypeUnmarshalerIface:
 			return d.parseToUnmarshaler(v)
 		}
@@ -1487,15 +1447,18 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 	case cborTypePrimitives:
 		_, ai, val := d.getHead()
 		switch ai {
-		case 25:
+		case additionalInformationAsFloat16:
 			f := float64(float16.Frombits(uint16(val)).Float32())
 			return fillFloat(t, f, v)
-		case 26:
+
+		case additionalInformationAsFloat32:
 			f := float64(math.Float32frombits(uint32(val)))
 			return fillFloat(t, f, v)
-		case 27:
+
+		case additionalInformationAsFloat64:
 			f := math.Float64frombits(val)
 			return fillFloat(t, f, v)
+
 		default: // ai <= 24
 			if d.dm.simpleValues.rejected[SimpleValue(val)] {
 				return &UnacceptableDataItemError{
@@ -1505,10 +1468,14 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 			}
 
 			switch ai {
-			case 20, 21:
-				return fillBool(t, ai == 21, v)
-			case 22, 23:
+			case additionalInformationAsFalse,
+				additionalInformationAsTrue:
+				return fillBool(t, ai == additionalInformationAsTrue, v)
+
+			case additionalInformationAsNull,
+				additionalInformationAsUndefined:
 				return fillNil(t, v)
+
 			default:
 				return fillPositiveInt(t, val, v)
 			}
@@ -1517,7 +1484,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 	case cborTypeTag:
 		_, _, tagNum := d.getHead()
 		switch tagNum {
-		case 2:
+		case tagNumUnsignedBignum:
 			// Bignum (tag 2) can be decoded to uint, int, float, slice, array, or big.Int.
 			b, copied := d.parseByteString()
 			bi := new(big.Int).SetBytes(b)
@@ -1537,7 +1504,8 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 				GoType:   tInfo.nonPtrType.String(),
 				errorMsg: bi.String() + " overflows " + v.Type().String(),
 			}
-		case 3:
+
+		case tagNumNegativeBignum:
 			// Bignum (tag 3) can be decoded to int, float, slice, array, or big.Int.
 			b, copied := d.parseByteString()
 			bi := new(big.Int).SetBytes(b)
@@ -1559,7 +1527,8 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 				GoType:   tInfo.nonPtrType.String(),
 				errorMsg: bi.String() + " overflows " + v.Type().String(),
 			}
-		case expectedLaterEncodingBase64URLTagNum, expectedLaterEncodingBase64TagNum, expectedLaterEncodingBase16TagNum:
+
+		case tagNumExpectedLaterEncodingBase64URL, tagNumExpectedLaterEncodingBase64, tagNumExpectedLaterEncodingBase16:
 			// If conversion for interoperability with text encodings is not configured,
 			// treat tags 21-23 as unregistered tags.
 			if d.dm.byteStringToString == ByteStringToStringAllowedWithExpectedLaterEncoding || d.dm.byteSliceExpectedEncoding != ByteSliceExpectedEncodingIgnored {
@@ -1569,6 +1538,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin
 				}()
 			}
 		}
+
 		return d.parseToValue(v, tInfo)
 
 	case cborTypeArray:
@@ -1662,6 +1632,7 @@ func (d *decoder) parseToTime() (time.Time, bool, error) {
 			return t, true, nil
 		}
 		return time.Time{}, false, &UnmarshalTypeError{CBORType: t.String(), GoType: typeTime.String()}
+
 	case cborTypeTextString:
 		s, err := d.parseTextString()
 		if err != nil {
@@ -1672,6 +1643,7 @@ func (d *decoder) parseToTime() (time.Time, bool, error) {
 			return time.Time{}, false, errors.New("cbor: cannot set " + string(s) + " for time.Time: " + err.Error())
 		}
 		return t, true, nil
+
 	case cborTypePositiveInt:
 		_, _, val := d.getHead()
 		if val > math.MaxInt64 {
@@ -1682,6 +1654,7 @@ func (d *decoder) parseToTime() (time.Time, bool, error) {
 			}
 		}
 		return time.Unix(int64(val), 0), true, nil
+
 	case cborTypeNegativeInt:
 		_, _, val := d.getHead()
 		if val > math.MaxInt64 {
@@ -1701,16 +1674,20 @@ func (d *decoder) parseToTime() (time.Time, bool, error) {
 			}
 		}
 		return time.Unix(int64(-1)^int64(val), 0), true, nil
+
 	case cborTypePrimitives:
 		_, ai, val := d.getHead()
 		var f float64
 		switch ai {
-		case 25:
+		case additionalInformationAsFloat16:
 			f = float64(float16.Frombits(uint16(val)).Float32())
-		case 26:
+
+		case additionalInformationAsFloat32:
 			f = float64(math.Float32frombits(uint32(val)))
-		case 27:
+
+		case additionalInformationAsFloat64:
 			f = math.Float64frombits(val)
+
 		default:
 			return time.Time{}, false, &UnmarshalTypeError{CBORType: t.String(), GoType: typeTime.String()}
 		}
@@ -1721,6 +1698,7 @@ func (d *decoder) parseToTime() (time.Time, bool, error) {
 		}
 		seconds, fractional := math.Modf(f)
 		return time.Unix(int64(seconds), int64(fractional*1e9)), true, nil
+
 	default:
 		return time.Time{}, false, &UnmarshalTypeError{CBORType: t.String(), GoType: typeTime.String()}
 	}
@@ -1754,7 +1732,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 		for d.nextCBORType() == cborTypeTag {
 			off := d.off
 			_, _, tagNum := d.getHead()
-			if tagNum != selfDescribedCBORTagNum {
+			if tagNum != tagNumSelfDescribedCBOR {
 				d.off = off
 				break
 			}
@@ -1853,8 +1831,10 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 			clone := make([]byte, len(b))
 			copy(clone, b)
 			return clone, nil
+
 		case typeString:
 			return string(b), nil
+
 		default:
 			if copied || d.dm.defaultByteStringType.Kind() == reflect.String {
 				// Avoid an unnecessary copy since the conversion to string must
@@ -1865,27 +1845,31 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 			copy(clone, b)
 			return reflect.ValueOf(clone).Convert(d.dm.defaultByteStringType).Interface(), nil
 		}
+
 	case cborTypeTextString:
 		b, err := d.parseTextString()
 		if err != nil {
 			return nil, err
 		}
 		return string(b), nil
+
 	case cborTypeTag:
 		tagOff := d.off
 		_, _, tagNum := d.getHead()
 		contentOff := d.off
 
 		switch tagNum {
-		case 0, 1:
+		case tagNumRFC3339Time, tagNumEpochTime:
 			d.off = tagOff
 			tm, _, err := d.parseToTime()
 			if err != nil {
 				return nil, err
 			}
+
 			switch d.dm.timeTagToAny {
 			case TimeTagToTime:
 				return tm, nil
+
 			case TimeTagToRFC3339:
 				if tagNum == 1 {
 					tm = tm.UTC()
@@ -1897,6 +1881,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 					return nil, err
 				}
 				return string(text), nil
+
 			case TimeTagToRFC3339Nano:
 				if tagNum == 1 {
 					tm = tm.UTC()
@@ -1908,10 +1893,12 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 					return nil, err
 				}
 				return string(text), nil
+
 			default:
 				// not reachable
 			}
-		case 2:
+
+		case tagNumUnsignedBignum:
 			b, _ := d.parseByteString()
 			bi := new(big.Int).SetBytes(b)
 
@@ -1919,7 +1906,8 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 				return bi, nil
 			}
 			return *bi, nil
-		case 3:
+
+		case tagNumNegativeBignum:
 			b, _ := d.parseByteString()
 			bi := new(big.Int).SetBytes(b)
 			bi.Add(bi, big.NewInt(1))
@@ -1929,7 +1917,8 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 				return bi, nil
 			}
 			return *bi, nil
-		case expectedLaterEncodingBase64URLTagNum, expectedLaterEncodingBase64TagNum, expectedLaterEncodingBase16TagNum:
+
+		case tagNumExpectedLaterEncodingBase64URL, tagNumExpectedLaterEncodingBase64, tagNumExpectedLaterEncodingBase16:
 			// If conversion for interoperability with text encodings is not configured,
 			// treat tags 21-23 as unregistered tags.
 			if d.dm.byteStringToString == ByteStringToStringAllowedWithExpectedLaterEncoding || d.dm.byteSliceExpectedEncoding != ByteSliceExpectedEncodingIgnored {
@@ -1969,6 +1958,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 			return content, nil
 		}
 		return Tag{tagNum, content}, nil
+
 	case cborTypePrimitives:
 		_, ai, val := d.getHead()
 		if ai <= 24 && d.dm.simpleValues.rejected[SimpleValue(val)] {
@@ -1980,23 +1970,32 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 		if ai < 20 || ai == 24 {
 			return SimpleValue(val), nil
 		}
+
 		switch ai {
-		case 20, 21:
-			return (ai == 21), nil
-		case 22, 23:
+		case additionalInformationAsFalse,
+			additionalInformationAsTrue:
+			return (ai == additionalInformationAsTrue), nil
+
+		case additionalInformationAsNull,
+			additionalInformationAsUndefined:
 			return nil, nil
-		case 25:
+
+		case additionalInformationAsFloat16:
 			f := float64(float16.Frombits(uint16(val)).Float32())
 			return f, nil
-		case 26:
+
+		case additionalInformationAsFloat32:
 			f := float64(math.Float32frombits(uint32(val)))
 			return f, nil
-		case 27:
+
+		case additionalInformationAsFloat64:
 			f := math.Float64frombits(val)
 			return f, nil
 		}
+
 	case cborTypeArray:
 		return d.parseArray()
+
 	case cborTypeMap:
 		if d.dm.defaultMapType != nil {
 			m := reflect.New(d.dm.defaultMapType)
@@ -2008,6 +2007,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 		}
 		return d.parseMap()
 	}
+
 	return nil, nil
 }
 
@@ -2016,8 +2016,8 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli
 // and only if the slice is backed by a copy of the input. Callers are
 // responsible for making a copy if necessary.
 func (d *decoder) parseByteString() ([]byte, bool) {
-	_, ai, val := d.getHead()
-	if ai != 31 {
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	if !indefiniteLength {
 		b := d.data[d.off : d.off+int(val)]
 		d.off += int(val)
 		return b, false
@@ -2036,7 +2036,14 @@ func (d *decoder) parseByteString() ([]byte, bool) {
 // encoding. If no transformation was performed (because it was not required), the original byte
 // slice is returned and the bool return value is false. Otherwise, a new slice containing the
 // converted bytes is returned along with the bool value true.
-func (d *decoder) applyByteStringTextConversion(src []byte, dstType reflect.Type) ([]byte, bool, error) {
+func (d *decoder) applyByteStringTextConversion(
+	src []byte,
+	dstType reflect.Type,
+) (
+	dst []byte,
+	transformed bool,
+	err error,
+) {
 	switch dstType.Kind() {
 	case reflect.String:
 		if d.dm.byteStringToString != ByteStringToStringAllowedWithExpectedLaterEncoding || len(d.expectedLaterEncodingTags) == 0 {
@@ -2044,23 +2051,27 @@ func (d *decoder) applyByteStringTextConversion(src []byte, dstType reflect.Type
 		}
 
 		switch d.expectedLaterEncodingTags[len(d.expectedLaterEncodingTags)-1] {
-		case expectedLaterEncodingBase64URLTagNum:
+		case tagNumExpectedLaterEncodingBase64URL:
 			encoded := make([]byte, base64.RawURLEncoding.EncodedLen(len(src)))
 			base64.RawURLEncoding.Encode(encoded, src)
 			return encoded, true, nil
-		case expectedLaterEncodingBase64TagNum:
+
+		case tagNumExpectedLaterEncodingBase64:
 			encoded := make([]byte, base64.StdEncoding.EncodedLen(len(src)))
 			base64.StdEncoding.Encode(encoded, src)
 			return encoded, true, nil
-		case expectedLaterEncodingBase16TagNum:
+
+		case tagNumExpectedLaterEncodingBase16:
 			encoded := make([]byte, hex.EncodedLen(len(src)))
 			hex.Encode(encoded, src)
 			return encoded, true, nil
+
 		default:
 			// If this happens, there is a bug: the decoder has pushed an invalid
 			// "expected later encoding" tag to the stack.
 			panic(fmt.Sprintf("unrecognized expected later encoding tag: %d", d.expectedLaterEncodingTags))
 		}
+
 	case reflect.Slice:
 		if dstType.Elem().Kind() != reflect.Uint8 || len(d.expectedLaterEncodingTags) > 0 {
 			// Either the destination is not a slice of bytes, or the encoder that
@@ -2077,6 +2088,7 @@ func (d *decoder) applyByteStringTextConversion(src []byte, dstType reflect.Type
 				return nil, false, fmt.Errorf("cbor: failed to decode base64url string: %v", err)
 			}
 			return decoded[:n], true, nil
+
 		case ByteSliceExpectedEncodingBase64:
 			decoded := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
 			n, err := base64.StdEncoding.Decode(decoded, src)
@@ -2084,6 +2096,7 @@ func (d *decoder) applyByteStringTextConversion(src []byte, dstType reflect.Type
 				return nil, false, fmt.Errorf("cbor: failed to decode base64 string: %v", err)
 			}
 			return decoded[:n], true, nil
+
 		case ByteSliceExpectedEncodingBase16:
 			decoded := make([]byte, hex.DecodedLen(len(src)))
 			n, err := hex.Decode(decoded, src)
@@ -2101,8 +2114,8 @@ func (d *decoder) applyByteStringTextConversion(src []byte, dstType reflect.Type
 // to prevent creating an extra copy of string.  Caller should wrap returned
 // byte slice as string when needed.
 func (d *decoder) parseTextString() ([]byte, error) {
-	_, ai, val := d.getHead()
-	if ai != 31 {
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	if !indefiniteLength {
 		b := d.data[d.off : d.off+int(val)]
 		d.off += int(val)
 		if d.dm.utf8 == UTF8RejectInvalid && !utf8.Valid(b) {
@@ -2128,8 +2141,8 @@ func (d *decoder) parseTextString() ([]byte, error) {
 }
 
 func (d *decoder) parseArray() ([]interface{}, error) {
-	_, ai, val := d.getHead()
-	hasSize := (ai != 31)
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	hasSize := !indefiniteLength
 	count := int(val)
 	if !hasSize {
 		count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance
@@ -2150,8 +2163,8 @@ func (d *decoder) parseArray() ([]interface{}, error) {
 }
 
 func (d *decoder) parseArrayToSlice(v reflect.Value, tInfo *typeInfo) error {
-	_, ai, val := d.getHead()
-	hasSize := (ai != 31)
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	hasSize := !indefiniteLength
 	count := int(val)
 	if !hasSize {
 		count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance
@@ -2172,8 +2185,8 @@ func (d *decoder) parseArrayToSlice(v reflect.Value, tInfo *typeInfo) error {
 }
 
 func (d *decoder) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error {
-	_, ai, val := d.getHead()
-	hasSize := (ai != 31)
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	hasSize := !indefiniteLength
 	count := int(val)
 	gi := 0
 	vLen := v.Len()
@@ -2202,8 +2215,8 @@ func (d *decoder) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error {
 }
 
 func (d *decoder) parseMap() (interface{}, error) {
-	_, ai, val := d.getHead()
-	hasSize := (ai != 31)
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	hasSize := !indefiniteLength
 	count := int(val)
 	m := make(map[interface{}]interface{})
 	var k, e interface{}
@@ -2267,8 +2280,8 @@ func (d *decoder) parseMap() (interface{}, error) {
 }
 
 func (d *decoder) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo
-	_, ai, val := d.getHead()
-	hasSize := (ai != 31)
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	hasSize := !indefiniteLength
 	count := int(val)
 	if v.IsNil() {
 		mapsize := count
@@ -2392,8 +2405,8 @@ func (d *decoder) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error {
 	}
 
 	start := d.off
-	t, ai, val := d.getHead()
-	hasSize := (ai != 31)
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	hasSize := !indefiniteLength
 	count := int(val)
 	if !hasSize {
 		count = d.numOfItemsUntilBreak() // peek ahead to get array size
@@ -2402,7 +2415,7 @@ func (d *decoder) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error {
 		d.off = start
 		d.skip()
 		return &UnmarshalTypeError{
-			CBORType: t.String(),
+			CBORType: cborTypeArray.String(),
 			GoType:   tInfo.typ.String(),
 			errorMsg: "cannot decode CBOR array to struct with different number of elements",
 		}
@@ -2467,8 +2480,8 @@ func (d *decoder) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //n
 	var err, lastErr error
 
 	// Get CBOR map size
-	_, ai, val := d.getHead()
-	hasSize := (ai != 31)
+	_, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
+	hasSize := !indefiniteLength
 	count := int(val)
 
 	// Keeps track of matched struct fields
@@ -2751,13 +2764,13 @@ func (d *decoder) getRegisteredTagItem(vt reflect.Type) *tagItem {
 // skip moves data offset to the next item.  skip assumes data is well-formed,
 // and does not perform bounds checking.
 func (d *decoder) skip() {
-	t, ai, val := d.getHead()
+	t, _, val, indefiniteLength := d.getHeadWithIndefiniteLengthFlag()
 
-	if ai == 31 {
+	if indefiniteLength {
 		switch t {
 		case cborTypeByteString, cborTypeTextString, cborTypeArray, cborTypeMap:
 			for {
-				if d.data[d.off] == 0xff {
+				if isBreakFlag(d.data[d.off]) {
 					d.off++
 					return
 				}
@@ -2769,47 +2782,67 @@ func (d *decoder) skip() {
 	switch t {
 	case cborTypeByteString, cborTypeTextString:
 		d.off += int(val)
+
 	case cborTypeArray:
 		for i := 0; i < int(val); i++ {
 			d.skip()
 		}
+
 	case cborTypeMap:
 		for i := 0; i < int(val)*2; i++ {
 			d.skip()
 		}
+
 	case cborTypeTag:
 		d.skip()
 	}
 }
 
+func (d *decoder) getHeadWithIndefiniteLengthFlag() (
+	t cborType,
+	ai byte,
+	val uint64,
+	indefiniteLength bool,
+) {
+	t, ai, val = d.getHead()
+	indefiniteLength = additionalInformation(ai).isIndefiniteLength()
+	return
+}
+
 // getHead assumes data is well-formed, and does not perform bounds checking.
 func (d *decoder) getHead() (t cborType, ai byte, val uint64) {
-	t = cborType(d.data[d.off] & 0xe0)
-	ai = d.data[d.off] & 0x1f
+	t, ai = parseInitialByte(d.data[d.off])
 	val = uint64(ai)
 	d.off++
 
-	if ai < 24 {
+	if ai <= maxAdditionalInformationWithoutArgument {
 		return
 	}
-	if ai == 24 {
+
+	if ai == additionalInformationWith1ByteArgument {
 		val = uint64(d.data[d.off])
 		d.off++
 		return
 	}
-	if ai == 25 {
-		val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2]))
-		d.off += 2
+
+	if ai == additionalInformationWith2ByteArgument {
+		const argumentSize = 2
+		val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+argumentSize]))
+		d.off += argumentSize
 		return
 	}
-	if ai == 26 {
-		val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4]))
-		d.off += 4
+
+	if ai == additionalInformationWith4ByteArgument {
+		const argumentSize = 4
+		val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+argumentSize]))
+		d.off += argumentSize
 		return
 	}
-	if ai == 27 {
-		val = binary.BigEndian.Uint64(d.data[d.off : d.off+8])
-		d.off += 8
+
+	if ai == additionalInformationWith8ByteArgument {
+		const argumentSize = 8
+		val = binary.BigEndian.Uint64(d.data[d.off : d.off+argumentSize])
+		d.off += argumentSize
 		return
 	}
 	return
@@ -2826,9 +2859,11 @@ func (d *decoder) numOfItemsUntilBreak() int {
 	return i
 }
 
+// foundBreak returns true if next byte is CBOR break code and moves cursor by 1,
+// otherwise it returns false.
 // foundBreak assumes data is well-formed, and does not perform bounds checking.
 func (d *decoder) foundBreak() bool {
-	if d.data[d.off] == 0xff {
+	if isBreakFlag(d.data[d.off]) {
 		d.off++
 		return true
 	}
@@ -2842,7 +2877,7 @@ func (d *decoder) reset(data []byte) {
 }
 
 func (d *decoder) nextCBORType() cborType {
-	return cborType(d.data[d.off] & 0xe0)
+	return getType(d.data[d.off])
 }
 
 func (d *decoder) nextCBORNil() bool {
@@ -2887,6 +2922,7 @@ func fillPositiveInt(t cborType, val uint64, v reflect.Value) error {
 		}
 		v.SetInt(int64(val))
 		return nil
+
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 		if v.OverflowUint(val) {
 			return &UnmarshalTypeError{
@@ -2897,11 +2933,13 @@ func fillPositiveInt(t cborType, val uint64, v reflect.Value) error {
 		}
 		v.SetUint(val)
 		return nil
+
 	case reflect.Float32, reflect.Float64:
 		f := float64(val)
 		v.SetFloat(f)
 		return nil
 	}
+
 	if v.Type() == typeBigInt {
 		i := new(big.Int).SetUint64(val)
 		v.Set(reflect.ValueOf(*i))
@@ -2922,6 +2960,7 @@ func fillNegativeInt(t cborType, val int64, v reflect.Value) error {
 		}
 		v.SetInt(val)
 		return nil
+
 	case reflect.Float32, reflect.Float64:
 		f := float64(val)
 		v.SetFloat(f)
@@ -3020,6 +3059,7 @@ func isImmutableKind(k reflect.Kind) bool {
 		reflect.Float32, reflect.Float64,
 		reflect.String:
 		return true
+
 	default:
 		return false
 	}
@@ -3029,6 +3069,7 @@ func isHashableValue(rv reflect.Value) bool {
 	switch rv.Kind() {
 	case reflect.Slice, reflect.Map, reflect.Func:
 		return false
+
 	case reflect.Struct:
 		switch rv.Type() {
 		case typeTag:
@@ -3051,6 +3092,7 @@ func convertByteSliceToByteString(v interface{}) (interface{}, bool) {
 	switch v := v.(type) {
 	case []byte:
 		return ByteString(v), true
+
 	case Tag:
 		content, converted := convertByteSliceToByteString(v.Content)
 		if converted {
@@ -3059,36 +3101,3 @@ func convertByteSliceToByteString(v interface{}) (interface{}, bool) {
 	}
 	return v, false
 }
-
-// validBuiltinTag checks that supported built-in tag numbers are followed by expected content types.
-func validBuiltinTag(tagNum uint64, contentHead byte) error {
-	t := cborType(contentHead & 0xe0)
-	switch tagNum {
-	case 0:
-		// Tag content (date/time text string in RFC 3339 format) must be string type.
-		if t != cborTypeTextString {
-			return errors.New("cbor: tag number 0 must be followed by text string, got " + t.String())
-		}
-		return nil
-	case 1:
-		// Tag content (epoch date/time) must be uint, int, or float type.
-		if t != cborTypePositiveInt && t != cborTypeNegativeInt && (contentHead < 0xf9 || contentHead > 0xfb) {
-			return errors.New("cbor: tag number 1 must be followed by integer or floating-point number, got " + t.String())
-		}
-		return nil
-	case 2, 3:
-		// Tag content (bignum) must be byte type.
-		if t != cborTypeByteString {
-			return errors.New("cbor: tag number 2 or 3 must be followed by byte string, got " + t.String())
-		}
-		return nil
-	case expectedLaterEncodingBase64URLTagNum, expectedLaterEncodingBase64TagNum, expectedLaterEncodingBase16TagNum:
-		// From RFC 8949 3.4.5.2:
-		//   The data item tagged can be a byte string or any other data item. In the latter
-		//   case, the tag applies to all of the byte string data items contained in the data
-		//   item, except for those contained in a nested data item tagged with an expected
-		//   conversion.
-		return nil
-	}
-	return nil
-}
diff --git a/diagnose.go b/diagnose.go
index ef406e0..2794df3 100644
--- a/diagnose.go
+++ b/diagnose.go
@@ -159,7 +159,7 @@ func (dm *diagMode) Diagnose(data []byte) (string, error) {
 }
 
 // DiagnoseFirst returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
-func (dm *diagMode) DiagnoseFirst(data []byte) (string, []byte, error) {
+func (dm *diagMode) DiagnoseFirst(data []byte) (diagNotation string, rest []byte, err error) {
 	return newDiagnose(data, dm.decMode, dm).diagFirst()
 }
 
@@ -174,7 +174,7 @@ func Diagnose(data []byte) (string, error) {
 }
 
 // Diagnose returns extended diagnostic notation (EDN) of the first CBOR data item using the DiagMode. Any remaining bytes are returned in rest.
-func DiagnoseFirst(data []byte) (string, []byte, error) {
+func DiagnoseFirst(data []byte) (diagNotation string, rest []byte, err error) {
 	return defaultDiagMode.DiagnoseFirst(data)
 }
 
@@ -202,8 +202,8 @@ func (di *diagnose) diag(cborSequence bool) (string, error) {
 				di.w.WriteString(", ")
 			}
 			firstItem = false
-			if err = di.item(); err != nil {
-				return di.w.String(), err
+			if itemErr := di.item(); itemErr != nil {
+				return di.w.String(), itemErr
 			}
 
 		case io.EOF:
@@ -218,8 +218,8 @@ func (di *diagnose) diag(cborSequence bool) (string, error) {
 	}
 }
 
-func (di *diagnose) diagFirst() (string, []byte, error) {
-	err := di.wellformed(true)
+func (di *diagnose) diagFirst() (diagNotation string, rest []byte, err error) {
+	err = di.wellformed(true)
 	if err == nil {
 		err = di.item()
 	}
@@ -242,16 +242,17 @@ func (di *diagnose) wellformed(allowExtraData bool) error {
 func (di *diagnose) item() error { //nolint:gocyclo
 	initialByte := di.d.data[di.d.off]
 	switch initialByte {
-	case 0x5f, 0x7f: // indefinite-length byte/text string
+	case cborByteStringWithIndefiniteLengthHead,
+		cborTextStringWithIndefiniteLengthHead: // indefinite-length byte/text string
 		di.d.off++
-		if di.d.data[di.d.off] == 0xff {
+		if isBreakFlag(di.d.data[di.d.off]) {
 			di.d.off++
 			switch initialByte {
-			case 0x5f:
+			case cborByteStringWithIndefiniteLengthHead:
 				// indefinite-length bytes with no chunks.
 				di.w.WriteString(`''_`)
 				return nil
-			case 0x7f:
+			case cborTextStringWithIndefiniteLengthHead:
 				// indefinite-length text with no chunks.
 				di.w.WriteString(`""_`)
 				return nil
@@ -276,7 +277,7 @@ func (di *diagnose) item() error { //nolint:gocyclo
 		di.w.WriteByte(')')
 		return nil
 
-	case 0x9f: // indefinite-length array
+	case cborArrayWithIndefiniteLengthHead: // indefinite-length array
 		di.d.off++
 		di.w.WriteString("[_ ")
 
@@ -295,7 +296,7 @@ func (di *diagnose) item() error { //nolint:gocyclo
 		di.w.WriteByte(']')
 		return nil
 
-	case 0xbf: // indefinite-length map
+	case cborMapWithIndefiniteLengthHead: // indefinite-length map
 		di.d.off++
 		di.w.WriteString("{_ ")
 
@@ -398,9 +399,13 @@ func (di *diagnose) item() error { //nolint:gocyclo
 	case cborTypeTag:
 		_, _, tagNum := di.d.getHead()
 		switch tagNum {
-		case 2:
+		case tagNumUnsignedBignum:
 			if nt := di.d.nextCBORType(); nt != cborTypeByteString {
-				return errors.New("cbor: tag number 2 must be followed by byte string, got " + nt.String())
+				return fmt.Errorf(
+					"cbor: tag number %d must be followed by byte string, got %s",
+					tagNumUnsignedBignum,
+					nt.String(),
+				)
 			}
 
 			b, _ := di.d.parseByteString()
@@ -408,9 +413,13 @@ func (di *diagnose) item() error { //nolint:gocyclo
 			di.w.WriteString(bi.String())
 			return nil
 
-		case 3:
+		case tagNumNegativeBignum:
 			if nt := di.d.nextCBORType(); nt != cborTypeByteString {
-				return errors.New("cbor: tag number 3 must be followed by byte string, got " + nt.String())
+				return fmt.Errorf(
+					"cbor: tag number %d must be followed by byte string, got %s",
+					tagNumNegativeBignum,
+					nt.String(),
+				)
 			}
 
 			b, _ := di.d.parseByteString()
@@ -433,23 +442,25 @@ func (di *diagnose) item() error { //nolint:gocyclo
 	case cborTypePrimitives:
 		_, ai, val := di.d.getHead()
 		switch ai {
-		case 20:
+		case additionalInformationAsFalse:
 			di.w.WriteString("false")
 			return nil
 
-		case 21:
+		case additionalInformationAsTrue:
 			di.w.WriteString("true")
 			return nil
 
-		case 22:
+		case additionalInformationAsNull:
 			di.w.WriteString("null")
 			return nil
 
-		case 23:
+		case additionalInformationAsUndefined:
 			di.w.WriteString("undefined")
 			return nil
 
-		case 25, 26, 27:
+		case additionalInformationAsFloat16,
+			additionalInformationAsFloat32,
+			additionalInformationAsFloat64:
 			return di.encodeFloat(ai, val)
 
 		default:
@@ -563,7 +574,7 @@ func (di *diagnose) encodeByteString(val []byte) error {
 	}
 }
 
-var utf16SurrSelf = rune(0x10000)
+const utf16SurrSelf = rune(0x10000)
 
 // quote should be either `'` or `"`
 func (di *diagnose) encodeTextString(val string, quote byte) error {
@@ -620,7 +631,7 @@ func (di *diagnose) encodeTextString(val string, quote byte) error {
 func (di *diagnose) encodeFloat(ai byte, val uint64) error {
 	f64 := float64(0)
 	switch ai {
-	case 25:
+	case additionalInformationAsFloat16:
 		f16 := float16.Frombits(uint16(val))
 		switch {
 		case f16.IsNaN():
@@ -636,7 +647,7 @@ func (di *diagnose) encodeFloat(ai byte, val uint64) error {
 			f64 = float64(f16.Float32())
 		}
 
-	case 26:
+	case additionalInformationAsFloat32:
 		f32 := math.Float32frombits(uint32(val))
 		switch {
 		case f32 != f32:
@@ -652,7 +663,7 @@ func (di *diagnose) encodeFloat(ai byte, val uint64) error {
 			f64 = float64(f32)
 		}
 
-	case 27:
+	case additionalInformationAsFloat64:
 		f64 = math.Float64frombits(val)
 		switch {
 		case f64 != f64:
@@ -668,16 +679,17 @@ func (di *diagnose) encodeFloat(ai byte, val uint64) error {
 	}
 	// Use ES6 number to string conversion which should match most JSON generators.
 	// Inspired by https://github.com/golang/go/blob/4df10fba1687a6d4f51d7238a403f8f2298f6a16/src/encoding/json/encode.go#L585
+	const bitSize = 64
 	b := make([]byte, 0, 32)
 	if abs := math.Abs(f64); abs != 0 && (abs < 1e-6 || abs >= 1e21) {
-		b = strconv.AppendFloat(b, f64, 'e', -1, 64)
+		b = strconv.AppendFloat(b, f64, 'e', -1, bitSize)
 		// clean up e-09 to e-9
 		n := len(b)
 		if n >= 4 && string(b[n-4:n-1]) == "e-0" {
 			b = append(b[:n-2], b[n-1])
 		}
 	} else {
-		b = strconv.AppendFloat(b, f64, 'f', -1, 64)
+		b = strconv.AppendFloat(b, f64, 'f', -1, bitSize)
 	}
 
 	// add decimal point and trailing zero if needed
@@ -695,13 +707,15 @@ func (di *diagnose) encodeFloat(ai byte, val uint64) error {
 
 	if di.dm.floatPrecisionIndicator {
 		switch ai {
-		case 25:
+		case additionalInformationAsFloat16:
 			di.w.WriteString("_1")
 			return nil
-		case 26:
+
+		case additionalInformationAsFloat32:
 			di.w.WriteString("_2")
 			return nil
-		case 27:
+
+		case additionalInformationAsFloat64:
 			di.w.WriteString("_3")
 			return nil
 		}
diff --git a/encode.go b/encode.go
index cd9430c..90a1a59 100644
--- a/encode.go
+++ b/encode.go
@@ -195,6 +195,7 @@ func (st StringMode) cborType() (cborType, error) {
 	switch st {
 	case StringToTextString:
 		return cborTypeTextString, nil
+
 	case StringToByteString:
 		return cborTypeByteString, nil
 	}
@@ -417,12 +418,15 @@ func (bsm ByteSliceMode) encodingTag() (uint64, error) {
 	switch bsm {
 	case ByteSliceToByteString:
 		return 0, nil
+
 	case ByteSliceToByteStringWithExpectedConversionToBase64URL:
-		return expectedLaterEncodingBase64URLTagNum, nil
+		return tagNumExpectedLaterEncodingBase64URL, nil
+
 	case ByteSliceToByteStringWithExpectedConversionToBase64:
-		return expectedLaterEncodingBase64TagNum, nil
+		return tagNumExpectedLaterEncodingBase64, nil
+
 	case ByteSliceToByteStringWithExpectedConversionToBase16:
-		return expectedLaterEncodingBase16TagNum, nil
+		return tagNumExpectedLaterEncodingBase16, nil
 	}
 	return 0, errors.New("cbor: invalid ByteSlice " + strconv.Itoa(int(bsm)))
 }
@@ -609,12 +613,12 @@ func PreferredUnsortedEncOptions() EncOptions {
 }
 
 // EncMode returns EncMode with immutable options and no tags (safe for concurrency).
-func (opts EncOptions) EncMode() (EncMode, error) {
+func (opts EncOptions) EncMode() (EncMode, error) { //nolint:gocritic // ignore hugeParam
 	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) {
+func (opts EncOptions) EncModeWithTags(tags TagSet) (EncMode, error) { //nolint:gocritic // ignore hugeParam
 	if opts.TagsMd == TagsForbidden {
 		return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden")
 	}
@@ -642,7 +646,7 @@ func (opts EncOptions) EncModeWithTags(tags TagSet) (EncMode, error) {
 }
 
 // EncModeWithSharedTags returns EncMode with immutable options and mutable shared tags (safe for concurrency).
-func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) {
+func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) { //nolint:gocritic // ignore hugeParam
 	if opts.TagsMd == TagsForbidden {
 		return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden")
 	}
@@ -657,7 +661,7 @@ func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) {
 	return em, nil
 }
 
-func (opts EncOptions) encMode() (*encMode, error) {
+func (opts EncOptions) encMode() (*encMode, error) { //nolint:gocritic // ignore hugeParam
 	if !opts.Sort.valid() {
 		return nil, errors.New("cbor: invalid SortMode " + strconv.Itoa(int(opts.Sort)))
 	}
@@ -913,15 +917,6 @@ func putEncodeBuffer(e *bytes.Buffer) {
 type encodeFunc func(e *bytes.Buffer, em *encMode, v reflect.Value) error
 type isEmptyFunc func(em *encMode, 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 *bytes.Buffer, em *encMode, v reflect.Value) error {
 	if !v.IsValid() {
 		// v is zero value
@@ -987,9 +982,9 @@ func encodeFloat(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 		// Encode float64
 		// Don't use encodeFloat64() because it cannot be inlined.
 		var scratch [9]byte
-		scratch[0] = byte(cborTypePrimitives) | byte(27)
+		scratch[0] = byte(cborTypePrimitives) | byte(additionalInformationAsFloat64)
 		binary.BigEndian.PutUint64(scratch[1:], math.Float64bits(f64))
-		e.Write(scratch[:9])
+		e.Write(scratch[:])
 		return nil
 	}
 
@@ -1011,7 +1006,7 @@ func encodeFloat(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 			// Encode float16
 			// Don't use encodeFloat16() because it cannot be inlined.
 			var scratch [3]byte
-			scratch[0] = byte(cborTypePrimitives) | byte(25)
+			scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat16
 			binary.BigEndian.PutUint16(scratch[1:], uint16(f16))
 			e.Write(scratch[:3])
 			return nil
@@ -1021,7 +1016,7 @@ func encodeFloat(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 	// Encode float32
 	// Don't use encodeFloat32() because it cannot be inlined.
 	var scratch [5]byte
-	scratch[0] = byte(cborTypePrimitives) | byte(26)
+	scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat32
 	binary.BigEndian.PutUint32(scratch[1:], math.Float32bits(f32))
 	e.Write(scratch[:5])
 	return nil
@@ -1032,6 +1027,7 @@ func encodeInf(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 	switch em.infConvert {
 	case InfConvertReject:
 		return &UnsupportedValueError{msg: "floating-point infinity"}
+
 	case InfConvertFloat16:
 		if f64 > 0 {
 			e.Write(cborPositiveInfinity)
@@ -1109,7 +1105,7 @@ func encodeNaN(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 
 func encodeFloat16(e *bytes.Buffer, f16 float16.Float16) error {
 	var scratch [3]byte
-	scratch[0] = byte(cborTypePrimitives) | byte(25)
+	scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat16
 	binary.BigEndian.PutUint16(scratch[1:], uint16(f16))
 	e.Write(scratch[:3])
 	return nil
@@ -1117,7 +1113,7 @@ func encodeFloat16(e *bytes.Buffer, f16 float16.Float16) error {
 
 func encodeFloat32(e *bytes.Buffer, f32 float32) error {
 	var scratch [5]byte
-	scratch[0] = byte(cborTypePrimitives) | byte(26)
+	scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat32
 	binary.BigEndian.PutUint32(scratch[1:], math.Float32bits(f32))
 	e.Write(scratch[:5])
 	return nil
@@ -1125,7 +1121,7 @@ func encodeFloat32(e *bytes.Buffer, f32 float32) error {
 
 func encodeFloat64(e *bytes.Buffer, f64 float64) error {
 	var scratch [9]byte
-	scratch[0] = byte(cborTypePrimitives) | byte(27)
+	scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat64
 	binary.BigEndian.PutUint64(scratch[1:], math.Float64bits(f64))
 	e.Write(scratch[:9])
 	return nil
@@ -1351,7 +1347,7 @@ func encodeStructToArray(e *bytes.Buffer, em *encMode, v reflect.Value) (err err
 			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) {
+			fv, _ = getFieldValue(v, f.idx, func(reflect.Value) (reflect.Value, error) {
 				// Write CBOR nil for null pointer to embedded struct
 				e.Write(cborNil)
 				return reflect.Value{}, nil
@@ -1399,7 +1395,7 @@ func encodeStruct(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) {
 			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) {
+			fv, _ = getFieldValue(v, f.idx, func(reflect.Value) (reflect.Value, error) {
 				// Skip null pointer to embedded struct
 				return reflect.Value{}, nil
 			})
@@ -1487,10 +1483,12 @@ func encodeTime(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 	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())
@@ -1499,9 +1497,11 @@ func encodeTime(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 		}
 		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))
@@ -1642,34 +1642,46 @@ func encodeTag(e *bytes.Buffer, em *encMode, v reflect.Value) error {
 
 // encodeHead writes CBOR head of specified type t and returns number of bytes written.
 func encodeHead(e *bytes.Buffer, t byte, n uint64) int {
-	if n <= 23 {
+	if n <= maxAdditionalInformationWithoutArgument {
+		const headSize = 1
 		e.WriteByte(t | byte(n))
-		return 1
+		return headSize
 	}
+
 	if n <= math.MaxUint8 {
-		scratch := [2]byte{t | byte(24), byte(n)}
-		e.Write(scratch[:2])
-		return 2
+		const headSize = 2
+		scratch := [headSize]byte{
+			t | byte(additionalInformationWith1ByteArgument),
+			byte(n),
+		}
+		e.Write(scratch[:])
+		return headSize
 	}
+
 	if n <= math.MaxUint16 {
-		var scratch [3]byte
-		scratch[0] = t | byte(25)
+		const headSize = 3
+		var scratch [headSize]byte
+		scratch[0] = t | byte(additionalInformationWith2ByteArgument)
 		binary.BigEndian.PutUint16(scratch[1:], uint16(n))
-		e.Write(scratch[:3])
-		return 3
+		e.Write(scratch[:])
+		return headSize
 	}
+
 	if n <= math.MaxUint32 {
-		var scratch [5]byte
-		scratch[0] = t | byte(26)
+		const headSize = 5
+		var scratch [headSize]byte
+		scratch[0] = t | byte(additionalInformationWith4ByteArgument)
 		binary.BigEndian.PutUint32(scratch[1:], uint32(n))
-		e.Write(scratch[:5])
-		return 5
+		e.Write(scratch[:])
+		return headSize
 	}
-	var scratch [9]byte
-	scratch[0] = t | byte(27)
+
+	const headSize = 9
+	var scratch [headSize]byte
+	scratch[0] = t | byte(additionalInformationWith8ByteArgument)
 	binary.BigEndian.PutUint64(scratch[1:], n)
-	e.Write(scratch[:9])
-	return 9
+	e.Write(scratch[:])
+	return headSize
 }
 
 var (
@@ -1687,14 +1699,19 @@ func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) {
 	switch t {
 	case typeSimpleValue:
 		return encodeMarshalerType, isEmptyUint
+
 	case typeTag:
 		return encodeTag, alwaysNotEmpty
+
 	case typeTime:
 		return encodeTime, alwaysNotEmpty
+
 	case typeBigInt:
 		return encodeBigInt, alwaysNotEmpty
+
 	case typeRawMessage:
 		return encodeMarshalerType, isEmptySlice
+
 	case typeByteString:
 		return encodeMarshalerType, isEmptyString
 	}
@@ -1715,31 +1732,39 @@ func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) {
 	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:
 		if t.Elem().Kind() == reflect.Uint8 {
 			return encodeByteString, isEmptySlice
 		}
 		fallthrough
+
 	case reflect.Array:
 		f, _ := getEncodeFunc(t.Elem())
 		if f == nil {
 			return nil, nil
 		}
 		return arrayEncodeFunc{f: f}.encode, isEmptySlice
+
 	case reflect.Map:
 		f := getEncodeMapFunc(t)
 		if f == nil {
 			return nil, nil
 		}
 		return f, isEmptyMap
+
 	case reflect.Struct:
 		// Get struct's special field "_" tag options
 		if f, ok := t.FieldByName("_"); ok {
@@ -1751,6 +1776,7 @@ func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) {
 			}
 		}
 		return encodeStruct, isEmptyStruct
+
 	case reflect.Interface:
 		return encodeIntf, isEmptyIntf
 	}
@@ -1844,7 +1870,7 @@ func isEmptyStruct(em *encMode, v reflect.Value) (bool, error) {
 			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) {
+			fv, _ = getFieldValue(v, f.idx, func(reflect.Value) (reflect.Value, error) {
 				// Skip null pointer to embedded struct
 				return reflect.Value{}, nil
 			})
diff --git a/simplevalue.go b/simplevalue.go
index 6f93f67..de175ce 100644
--- a/simplevalue.go
+++ b/simplevalue.go
@@ -33,11 +33,11 @@ func (sv SimpleValue) MarshalCBOR() ([]byte, error) {
 	// only has a single representation variant)."
 
 	switch {
-	case sv <= 23:
+	case sv <= maxSimpleValueInAdditionalInformation:
 		return []byte{byte(cborTypePrimitives) | byte(sv)}, nil
 
-	case sv >= 32:
-		return []byte{byte(cborTypePrimitives) | byte(24), byte(sv)}, nil
+	case sv >= minSimpleValueIn1ByteArgument:
+		return []byte{byte(cborTypePrimitives) | additionalInformationWith1ByteArgument, byte(sv)}, nil
 
 	default:
 		return nil, &UnsupportedValueError{msg: fmt.Sprintf("SimpleValue(%d)", sv)}
@@ -57,7 +57,7 @@ func (sv *SimpleValue) UnmarshalCBOR(data []byte) error {
 	if typ != cborTypePrimitives {
 		return &UnmarshalTypeError{CBORType: typ.String(), GoType: "SimpleValue"}
 	}
-	if ai > 24 {
+	if ai > additionalInformationWith1ByteArgument {
 		return &UnmarshalTypeError{CBORType: typ.String(), GoType: "SimpleValue", errorMsg: "not simple values"}
 	}
 
diff --git a/stream.go b/stream.go
index 4091d88..507ab6c 100644
--- a/stream.go
+++ b/stream.go
@@ -231,7 +231,7 @@ func (enc *Encoder) EndIndefinite() error {
 	if len(enc.indefTypes) == 0 {
 		return errors.New("cbor: cannot encode \"break\" code outside indefinite length values")
 	}
-	_, err := enc.w.Write([]byte{0xff})
+	_, err := enc.w.Write([]byte{cborBreakFlag})
 	if err == nil {
 		enc.indefTypes = enc.indefTypes[:len(enc.indefTypes)-1]
 	}
@@ -239,10 +239,10 @@ func (enc *Encoder) EndIndefinite() error {
 }
 
 var cborIndefHeader = map[cborType][]byte{
-	cborTypeByteString: {0x5f},
-	cborTypeTextString: {0x7f},
-	cborTypeArray:      {0x9f},
-	cborTypeMap:        {0xbf},
+	cborTypeByteString: {cborByteStringWithIndefiniteLengthHead},
+	cborTypeTextString: {cborTextStringWithIndefiniteLengthHead},
+	cborTypeArray:      {cborArrayWithIndefiniteLengthHead},
+	cborTypeMap:        {cborMapWithIndefiniteLengthHead},
 }
 
 func (enc *Encoder) startIndefinite(typ cborType) error {
diff --git a/structfields.go b/structfields.go
index 23a12be..81228ac 100644
--- a/structfields.go
+++ b/structfields.go
@@ -144,7 +144,15 @@ func getFields(t reflect.Type) (flds fields, structOptions string) {
 }
 
 // appendFields appends type t's exportable fields to flds and anonymous struct fields to nTypes .
-func appendFields(t reflect.Type, idx []int, flds fields, nTypes map[reflect.Type][][]int) (fields, map[reflect.Type][][]int) {
+func appendFields(
+	t reflect.Type,
+	idx []int,
+	flds fields,
+	nTypes map[reflect.Type][][]int,
+) (
+	_flds fields,
+	_nTypes map[reflect.Type][][]int,
+) {
 	for i := 0; i < t.NumField(); i++ {
 		f := t.Field(i)
 
@@ -165,12 +173,12 @@ func appendFields(t reflect.Type, idx []int, flds fields, nTypes map[reflect.Typ
 			continue
 		}
 
-		tagged := len(tag) > 0
+		tagged := tag != ""
 
 		// Parse field tag options
 		var tagFieldName string
 		var omitempty, keyasint bool
-		for j := 0; len(tag) > 0; j++ {
+		for j := 0; tag != ""; j++ {
 			var token string
 			idx := strings.IndexByte(tag, ',')
 			if idx == -1 {
@@ -199,7 +207,7 @@ func appendFields(t reflect.Type, idx []int, flds fields, nTypes map[reflect.Typ
 		copy(fIdx, idx)
 		fIdx[len(fIdx)-1] = i
 
-		if !f.Anonymous || ft.Kind() != reflect.Struct || len(tagFieldName) > 0 {
+		if !f.Anonymous || ft.Kind() != reflect.Struct || tagFieldName != "" {
 			flds = append(flds, &field{
 				name:      fieldName,
 				idx:       fIdx,
@@ -221,7 +229,7 @@ func appendFields(t reflect.Type, idx []int, flds fields, nTypes map[reflect.Typ
 // isFieldExportable returns true if f is an exportable (regular or anonymous) field or
 // a nonexportable anonymous field of struct type.
 // Nonexportable anonymous field of struct type can contain exportable fields.
-func isFieldExportable(f reflect.StructField, fk reflect.Kind) bool {
+func isFieldExportable(f reflect.StructField, fk reflect.Kind) bool { //nolint:gocritic // ignore hugeParam
 	exportable := f.PkgPath == ""
 	return exportable || (f.Anonymous && fk == reflect.Struct)
 }
diff --git a/tag.go b/tag.go
index 4fade06..4c9c99f 100644
--- a/tag.go
+++ b/tag.go
@@ -261,7 +261,7 @@ func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum
 	if num == 2 || num == 3 {
 		return nil, errors.New("cbor: cannot add tag number 2 or 3 to TagSet, it's built-in and supported automatically")
 	}
-	if num == selfDescribedCBORTagNum {
+	if num == tagNumSelfDescribedCBOR {
 		return nil, errors.New("cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically")
 	}
 
diff --git a/valid.go b/valid.go
index 2fa5497..b40793b 100644
--- a/valid.go
+++ b/valid.go
@@ -99,15 +99,15 @@ func (d *decoder) wellformed(allowExtraData bool, checkBuiltinTags bool) error {
 }
 
 // wellformedInternal checks data's well-formedness and returns max depth and error.
-func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, error) { // nolint:gocyclo
-	t, ai, val, err := d.wellformedHead()
+func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, error) { //nolint:gocyclo
+	t, _, val, indefiniteLength, err := d.wellformedHeadWithIndefiniteLengthFlag()
 	if err != nil {
 		return 0, err
 	}
 
 	switch t {
 	case cborTypeByteString, cborTypeTextString:
-		if ai == 31 {
+		if indefiniteLength {
 			if d.dm.indefLength == IndefLengthForbidden {
 				return 0, &IndefiniteLengthError{t}
 			}
@@ -129,7 +129,7 @@ func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, err
 			return 0, &MaxNestedLevelError{d.dm.maxNestedLevels}
 		}
 
-		if ai == 31 {
+		if indefiniteLength {
 			if d.dm.indefLength == IndefLengthForbidden {
 				return 0, &IndefiniteLengthError{t}
 			}
@@ -194,7 +194,7 @@ func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, err
 					Message:  "bignum",
 				}
 			}
-			if cborType(d.data[d.off]&0xe0) != cborTypeTag {
+			if getType(d.data[d.off]) != cborTypeTag {
 				break
 			}
 			if _, _, tagNum, err = d.wellformedHead(); err != nil {
@@ -219,16 +219,16 @@ func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltin
 		if len(d.data) == d.off {
 			return 0, io.ErrUnexpectedEOF
 		}
-		if d.data[d.off] == 0xff {
+		if isBreakFlag(d.data[d.off]) {
 			d.off++
 			break
 		}
 		// Peek ahead to get next type and indefinite length status.
-		nt := cborType(d.data[d.off] & 0xe0)
+		nt, ai := parseInitialByte(d.data[d.off])
 		if t != nt {
 			return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()}
 		}
-		if (d.data[d.off] & 0x1f) == 31 {
+		if additionalInformation(ai).isIndefiniteLength() {
 			return 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"}
 		}
 		if depth, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
@@ -247,7 +247,7 @@ func (d *decoder) wellformedIndefiniteArrayOrMap(t cborType, depth int, checkBui
 		if len(d.data) == d.off {
 			return 0, io.ErrUnexpectedEOF
 		}
-		if d.data[d.off] == 0xff {
+		if isBreakFlag(d.data[d.off]) {
 			d.off++
 			break
 		}
@@ -275,22 +275,39 @@ func (d *decoder) wellformedIndefiniteArrayOrMap(t cborType, depth int, checkBui
 	return maxDepth, nil
 }
 
+func (d *decoder) wellformedHeadWithIndefiniteLengthFlag() (
+	t cborType,
+	ai byte,
+	val uint64,
+	indefiniteLength bool,
+	err error,
+) {
+	t, ai, val, err = d.wellformedHead()
+	if err != nil {
+		return
+	}
+	indefiniteLength = additionalInformation(ai).isIndefiniteLength()
+	return
+}
+
 func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) {
 	dataLen := len(d.data) - d.off
 	if dataLen == 0 {
 		return 0, 0, 0, io.ErrUnexpectedEOF
 	}
 
-	t = cborType(d.data[d.off] & 0xe0)
-	ai = d.data[d.off] & 0x1f
+	t, ai = parseInitialByte(d.data[d.off])
 	val = uint64(ai)
 	d.off++
+	dataLen--
 
-	if ai < 24 {
+	if ai <= maxAdditionalInformationWithoutArgument {
 		return t, ai, val, nil
 	}
-	if ai == 24 {
-		if dataLen < 2 {
+
+	if ai == additionalInformationWith1ByteArgument {
+		const argumentSize = 1
+		if dataLen < argumentSize {
 			return 0, 0, 0, io.ErrUnexpectedEOF
 		}
 		val = uint64(d.data[d.off])
@@ -300,12 +317,14 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
 		}
 		return t, ai, val, nil
 	}
-	if ai == 25 {
-		if dataLen < 3 {
+
+	if ai == additionalInformationWith2ByteArgument {
+		const argumentSize = 2
+		if dataLen < argumentSize {
 			return 0, 0, 0, io.ErrUnexpectedEOF
 		}
-		val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2]))
-		d.off += 2
+		val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+argumentSize]))
+		d.off += argumentSize
 		if t == cborTypePrimitives {
 			if err := d.acceptableFloat(float64(float16.Frombits(uint16(val)).Float32())); err != nil {
 				return 0, 0, 0, err
@@ -313,12 +332,14 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
 		}
 		return t, ai, val, nil
 	}
-	if ai == 26 {
-		if dataLen < 5 {
+
+	if ai == additionalInformationWith4ByteArgument {
+		const argumentSize = 4
+		if dataLen < argumentSize {
 			return 0, 0, 0, io.ErrUnexpectedEOF
 		}
-		val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4]))
-		d.off += 4
+		val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+argumentSize]))
+		d.off += argumentSize
 		if t == cborTypePrimitives {
 			if err := d.acceptableFloat(float64(math.Float32frombits(uint32(val)))); err != nil {
 				return 0, 0, 0, err
@@ -326,12 +347,14 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
 		}
 		return t, ai, val, nil
 	}
-	if ai == 27 {
-		if dataLen < 9 {
+
+	if ai == additionalInformationWith8ByteArgument {
+		const argumentSize = 8
+		if dataLen < argumentSize {
 			return 0, 0, 0, io.ErrUnexpectedEOF
 		}
-		val = binary.BigEndian.Uint64(d.data[d.off : d.off+8])
-		d.off += 8
+		val = binary.BigEndian.Uint64(d.data[d.off : d.off+argumentSize])
+		d.off += argumentSize
 		if t == cborTypePrimitives {
 			if err := d.acceptableFloat(math.Float64frombits(val)); err != nil {
 				return 0, 0, 0, err
@@ -339,7 +362,8 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
 		}
 		return t, ai, val, nil
 	}
-	if ai == 31 {
+
+	if additionalInformation(ai).isIndefiniteLength() {
 		switch t {
 		case cborTypePositiveInt, cborTypeNegativeInt, cborTypeTag:
 			return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
@@ -348,6 +372,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error)
 		}
 		return t, ai, val, nil
 	}
+
 	// ai == 28, 29, 30
 	return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
 }


Test patch:
diff --git a/bench_test.go b/bench_test.go
index b88249b..5e64a83 100644
--- a/bench_test.go
+++ b/bench_test.go
@@ -864,7 +864,7 @@ func BenchmarkUnmarshalMapToStruct(b *testing.B) {
 		reject bool
 	}
 
-	for _, tc := range []struct {
+	for _, tc := range []*struct {
 		name   string
 		opts   DecOptions
 		inputs []input
diff --git a/decode_test.go b/decode_test.go
index 440f53e..6eebb94 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -8255,7 +8255,7 @@ func TestDecodeBignumToEmptyInterface(t *testing.T) {
 			err := decMode.Unmarshal(tc.data, &v)
 			if err != nil {
 				t.Errorf("Unmarshal(0x%x) to empty interface returned error %v", tc.data, err)
-			} else {
+			} else { //nolint:gocritic // ignore elseif
 				if !reflect.DeepEqual(v, tc.wantValue) {
 					t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.data, v, v, tc.wantValue, tc.wantValue)
 				}
@@ -8706,10 +8706,11 @@ func TestUnmarshalSimpleValues(t *testing.T) {
 
 	assertExactError := func(want error) func(*testing.T, error) {
 		return func(t *testing.T, got error) {
-			if reflect.DeepEqual(want, got) {
-				return
+			targetErr := reflect.New(reflect.TypeOf(want)).Interface()
+			if !errors.As(got, &targetErr) ||
+				got.Error() != want.Error() {
+				t.Errorf("want %#v, got %#v", want, got)
 			}
-			t.Errorf("want %#v, got %#v", want, got)
 		}
 	}
 
diff --git a/encode_test.go b/encode_test.go
index 9ca2a43..2cc8837 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -328,7 +328,8 @@ func TestInvalidTypeMarshal(t *testing.T) {
 	}
 	for _, tc := range marshalErrorTests {
 		t.Run(tc.name, func(t *testing.T) {
-			b, err := Marshal(&tc.value)
+			v := tc.value
+			b, err := Marshal(&v)
 			if err == nil {
 				t.Errorf("Marshal(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg)
 			} else if _, ok := err.(*UnsupportedTypeError); !ok {
@@ -339,7 +340,8 @@ func TestInvalidTypeMarshal(t *testing.T) {
 				t.Errorf("Marshal(%v) = 0x%x, want nil", tc.value, b)
 			}
 
-			b, err = em.Marshal(&tc.value)
+			v = tc.value
+			b, err = em.Marshal(&v)
 			if err == nil {
 				t.Errorf("Marshal(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg)
 			} else if _, ok := err.(*UnsupportedTypeError); !ok {
@@ -449,27 +451,46 @@ func TestMarshalLargeMap(t *testing.T) {
 }
 
 func encodeCborHeader(t cborType, n uint64) []byte {
-	b := make([]byte, 9)
-	if n <= 23 {
+	if n <= maxAdditionalInformationWithoutArgument {
+		const headSize = 1
+		var b [headSize]byte
 		b[0] = byte(t) | byte(n)
-		return b[:1]
-	} else if n <= math.MaxUint8 {
-		b[0] = byte(t) | byte(24)
+		return b[:]
+	}
+
+	if n <= math.MaxUint8 {
+		const argumentSize = 1
+		const headSize = 1 + argumentSize
+		var b [headSize]byte
+		b[0] = byte(t) | additionalInformationWith1ByteArgument
 		b[1] = byte(n)
-		return b[:2]
-	} else if n <= math.MaxUint16 {
-		b[0] = byte(t) | byte(25)
+		return b[:]
+	}
+
+	if n <= math.MaxUint16 {
+		const argumentSize = 2
+		const headSize = 1 + argumentSize
+		var b [headSize]byte
+		b[0] = byte(t) | additionalInformationWith2ByteArgument
 		binary.BigEndian.PutUint16(b[1:], uint16(n))
-		return b[:3]
-	} else if n <= math.MaxUint32 {
-		b[0] = byte(t) | byte(26)
+		return b[:]
+	}
+
+	if n <= math.MaxUint32 {
+		const argumentSize = 4
+		const headSize = 1 + argumentSize
+		var b [headSize]byte
+		b[0] = byte(t) | additionalInformationWith4ByteArgument
 		binary.BigEndian.PutUint32(b[1:], uint32(n))
-		return b[:5]
-	} else {
-		b[0] = byte(t) | byte(27)
-		binary.BigEndian.PutUint64(b[1:], n)
-		return b[:9]
+		return b[:]
 	}
+
+	const argumentSize = 8
+	const headSize = 1 + argumentSize
+	var b [headSize]byte
+	b[0] = byte(t) | additionalInformationWith8ByteArgument
+	binary.BigEndian.PutUint64(b[1:], n)
+	return b[:]
 }
 
 func testMarshal(t *testing.T, testCases []marshalTest) {
@@ -4467,7 +4488,7 @@ func TestSortModeFastShuffle(t *testing.T) {
 				if err != nil {
 					t.Fatal(err)
 				}
-				if string(first) != string(next) {
+				if !bytes.Equal(first, next) {
 					return
 				}
 			}
@@ -4570,7 +4591,7 @@ func TestMarshalByteArrayMode(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			if string(out) != string(tc.expected) {
+			if !bytes.Equal(out, tc.expected) {
 				t.Errorf("unexpected output, got 0x%x want 0x%x", out, tc.expected)
 			}
 		})
@@ -4669,7 +4690,7 @@ func TestMarshalByteSliceMode(t *testing.T) {
 				t.Fatal(err)
 			}
 
-			if string(out) != string(tc.expected) {
+			if !bytes.Equal(out, tc.expected) {
 				t.Errorf("unexpected output, got 0x%x want 0x%x", out, tc.expected)
 			}
 		})
diff --git a/stream_test.go b/stream_test.go
index 06194b2..8e732e9 100644
--- a/stream_test.go
+++ b/stream_test.go
@@ -699,7 +699,8 @@ func TestEncoderError(t *testing.T) {
 	encoder := NewEncoder(&w)
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
-			err := encoder.Encode(&tc.value)
+			v := tc.value
+			err := encoder.Encode(&v)
 			if err == nil {
 				t.Errorf("Encode(%v) didn't return an error, want error %q", tc.value, tc.wantErrorMsg)
 			} else if _, ok := err.(*UnsupportedTypeError); !ok {
diff --git a/tag_test.go b/tag_test.go
index dec3181..fd9246d 100644
--- a/tag_test.go
+++ b/tag_test.go
@@ -1388,7 +1388,7 @@ func (n *number3) UnmarshalCBOR(data []byte) (err error) {
 		return fmt.Errorf("wrong tag number %d, want %d", rawTag.Number, 100)
 	}
 
-	if rawTag.Content[0]&0xe0 != 0xa0 {
+	if getType(rawTag.Content[0]) != cborTypeMap {
 		return fmt.Errorf("wrong tag content type, want map")
 	}