From 03575b4950291d5f3cef2503529cbc69013640d4 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 15:33:34 -0500 Subject: [PATCH 01/10] Refactor code and lint --- bench_test.go | 2 +- decode.go | 21 ++++++++++++++------- decode_test.go | 9 +++++---- diagnose.go | 12 ++++++------ encode.go | 8 ++++---- encode_test.go | 6 +++--- structfields.go | 12 ++++++++++-- 7 files changed, 43 insertions(+), 27 deletions(-) diff --git a/bench_test.go b/bench_test.go index b88249b4..5e64a83e 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.go b/decode.go index 66599e13..96003cb4 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") } @@ -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))) } @@ -1334,7 +1334,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 { @@ -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 { diff --git a/decode_test.go b/decode_test.go index 440f53ea..6eebb946 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/diagnose.go b/diagnose.go index ef406e02..a3a8cbd0 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() } diff --git a/encode.go b/encode.go index cd9430cb..5440300a 100644 --- a/encode.go +++ b/encode.go @@ -609,12 +609,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 +642,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 +657,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))) } diff --git a/encode_test.go b/encode_test.go index 9ca2a432..463bef88 100644 --- a/encode_test.go +++ b/encode_test.go @@ -4467,7 +4467,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 +4570,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 +4669,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/structfields.go b/structfields.go index 23a12bee..d2a2c1d1 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) @@ -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) } From b75278bc2e233337eb09d15d5c5f9568cb588d10 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 16:06:38 -0500 Subject: [PATCH 02/10] Refactor CBOR head funcs to remove magic numbers --- cache.go | 3 ++- decode.go | 69 +++++++++++++++++++++++++++++++++++++++++------------ encode.go | 46 ++++++++++++++++++++++------------- tag_test.go | 2 +- valid.go | 52 ++++++++++++++++++++++++---------------- 5 files changed, 117 insertions(+), 55 deletions(-) diff --git a/cache.go b/cache.go index 8b4d4631..ea0f39e2 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/decode.go b/decode.go index 96003cb4..4760d692 100644 --- a/decode.go +++ b/decode.go @@ -1311,6 +1311,39 @@ func (t cborType) String() string { } } +const ( + // From RFC 8949.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(b byte) cborType { + return cborType(b & typeMask) +} + +func getAdditionalInformation(b byte) byte { + return b & additionalInformationMask +} + +func parseInitialByte(b byte) (t cborType, ai byte) { + return getType(b), getAdditionalInformation(b) +} + +const ( + maxAdditionalInformationWithoutArgument = 23 + additionalInformationWith1ByteArgument = 24 + additionalInformationWith2ByteArgument = 25 + additionalInformationWith4ByteArgument = 26 + additionalInformationWith8ByteArgument = 27 +) + const ( selfDescribedCBORTagNum = 55799 expectedLaterEncodingBase64URLTagNum = 21 @@ -2791,32 +2824,38 @@ func (d *decoder) skip() { // 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 @@ -2849,7 +2888,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 { @@ -3069,7 +3108,7 @@ func convertByteSliceToByteString(v interface{}) (interface{}, bool) { // 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) + t := getType(contentHead) switch tagNum { case 0: // Tag content (date/time text string in RFC 3339 format) must be string type. diff --git a/encode.go b/encode.go index 5440300a..f1e720a0 100644 --- a/encode.go +++ b/encode.go @@ -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 ( diff --git a/tag_test.go b/tag_test.go index dec31815..fd9246d1 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") } diff --git a/valid.go b/valid.go index 2fa54978..a60f6139 100644 --- a/valid.go +++ b/valid.go @@ -99,7 +99,7 @@ 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 +func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, error) { //nolint:gocyclo t, ai, val, err := d.wellformedHead() if err != nil { return 0, err @@ -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 { @@ -224,11 +224,11 @@ func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltin 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 ai == 31 { return 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"} } if depth, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil { @@ -281,16 +281,18 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) 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 +302,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 +317,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 +332,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,6 +347,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) } return t, ai, val, nil } + if ai == 31 { switch t { case cborTypePositiveInt, cborTypeNegativeInt, cborTypeTag: @@ -348,6 +357,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()} } From 6d25a5c62422fb00e12fd7576833cd876f02d8ff Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 18:03:56 -0500 Subject: [PATCH 03/10] Refactor to remove magic number 31 (indef length) --- decode.go | 76 +++++++++++++++++++++++++++++++++---------------------- valid.go | 25 ++++++++++++++---- 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/decode.go b/decode.go index 4760d692..ba58bca0 100644 --- a/decode.go +++ b/decode.go @@ -1324,12 +1324,16 @@ const ( additionalInformationMask = 0x1f ) -func getType(b byte) cborType { - return cborType(b & typeMask) +func getType(raw byte) cborType { + return cborType(raw & typeMask) } -func getAdditionalInformation(b byte) byte { - return b & additionalInformationMask +func getAdditionalInformation(raw byte) byte { + return raw & additionalInformationMask +} + +func isIndefiniteLength(ai byte) bool { + return ai == additionalInformationAsIndefiniteLengthFlag } func parseInitialByte(b byte) (t cborType, ai byte) { @@ -1337,11 +1341,12 @@ func parseInitialByte(b byte) (t cborType, ai byte) { } const ( - maxAdditionalInformationWithoutArgument = 23 - additionalInformationWith1ByteArgument = 24 - additionalInformationWith2ByteArgument = 25 - additionalInformationWith4ByteArgument = 26 - additionalInformationWith8ByteArgument = 27 + maxAdditionalInformationWithoutArgument = 23 + additionalInformationWith1ByteArgument = 24 + additionalInformationWith2ByteArgument = 25 + additionalInformationWith4ByteArgument = 26 + additionalInformationWith8ByteArgument = 27 + additionalInformationAsIndefiniteLengthFlag = 31 ) const ( @@ -2049,8 +2054,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 @@ -2141,8 +2146,8 @@ func (d *decoder) applyByteStringTextConversion( // 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) { @@ -2168,8 +2173,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 @@ -2190,8 +2195,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 @@ -2212,8 +2217,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() @@ -2242,8 +2247,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{} @@ -2307,8 +2312,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 @@ -2432,8 +2437,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 @@ -2442,7 +2447,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", } @@ -2507,8 +2512,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 @@ -2791,9 +2796,9 @@ 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 { @@ -2822,6 +2827,17 @@ func (d *decoder) skip() { } } +func (d *decoder) getHeadWithIndefiniteLengthFlag() ( + t cborType, + ai byte, + val uint64, + indefiniteLength bool, +) { + t, ai, val = d.getHead() + indefiniteLength = isIndefiniteLength(ai) + return +} + // getHead assumes data is well-formed, and does not perform bounds checking. func (d *decoder) getHead() (t cborType, ai byte, val uint64) { t, ai = parseInitialByte(d.data[d.off]) diff --git a/valid.go b/valid.go index a60f6139..95dc74ee 100644 --- a/valid.go +++ b/valid.go @@ -100,14 +100,14 @@ 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() + 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} } @@ -228,7 +228,7 @@ func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltin if t != nt { return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()} } - if ai == 31 { + if isIndefiniteLength(ai) { return 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"} } if depth, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil { @@ -275,6 +275,21 @@ 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 = isIndefiniteLength(ai) + return +} + func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) { dataLen := len(d.data) - d.off if dataLen == 0 { @@ -348,7 +363,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) return t, ai, val, nil } - if ai == 31 { + if isIndefiniteLength(ai) { switch t { case cborTypePositiveInt, cborTypeNegativeInt, cborTypeTag: return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()} From 23ddf31ad6ef8f11ae442069b097453ded43c0bb Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 18:22:34 -0500 Subject: [PATCH 04/10] Refactor to remove magic number 0xff (break flag) --- decode.go | 10 ++++++++-- diagnose.go | 2 +- encode.go | 1 + stream.go | 2 +- valid.go | 4 ++-- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/decode.go b/decode.go index ba58bca0..d01e26b5 100644 --- a/decode.go +++ b/decode.go @@ -1336,6 +1336,10 @@ func isIndefiniteLength(ai byte) bool { return ai == additionalInformationAsIndefiniteLengthFlag } +func isBreakFlag(raw byte) bool { + return raw == cborBreakFlag +} + func parseInitialByte(b byte) (t cborType, ai byte) { return getType(b), getAdditionalInformation(b) } @@ -2802,7 +2806,7 @@ func (d *decoder) skip() { switch t { case cborTypeByteString, cborTypeTextString, cborTypeArray, cborTypeMap: for { - if d.data[d.off] == 0xff { + if isBreakFlag(d.data[d.off]) { d.off++ return } @@ -2888,9 +2892,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 } diff --git a/diagnose.go b/diagnose.go index a3a8cbd0..cba46fff 100644 --- a/diagnose.go +++ b/diagnose.go @@ -244,7 +244,7 @@ func (di *diagnose) item() error { //nolint:gocyclo switch initialByte { case 0x5f, 0x7f: // 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: diff --git a/encode.go b/encode.go index f1e720a0..8882e1e9 100644 --- a/encode.go +++ b/encode.go @@ -914,6 +914,7 @@ 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 ( + cborBreakFlag = byte(0xff) cborFalse = []byte{0xf4} cborTrue = []byte{0xf5} cborNil = []byte{0xf6} diff --git a/stream.go b/stream.go index 4091d882..dcb60b44 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] } diff --git a/valid.go b/valid.go index 95dc74ee..fa7f1045 100644 --- a/valid.go +++ b/valid.go @@ -219,7 +219,7 @@ 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 } @@ -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 } From 22175debe5e23d07dff875f9326a318046e8dabe Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 18:52:21 -0500 Subject: [PATCH 05/10] Extract CBOR vars & funcs to new file cbor.go --- cbor.go | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++ decode.go | 140 +++++------------------------------------------------- encode.go | 16 ++----- tag.go | 2 +- valid.go | 6 +-- 5 files changed, 157 insertions(+), 146 deletions(-) create mode 100644 cbor.go diff --git a/cbor.go b/cbor.go new file mode 100644 index 00000000..12c3e5be --- /dev/null +++ b/cbor.go @@ -0,0 +1,139 @@ +// Copyright (c) Faye Amacker. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +package cbor + +import ( + "errors" + "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 + additionalInformationAsIndefiniteLengthFlag = 31 +) + +func (ai additionalInformation) isIndefiniteLength() bool { + return ai == additionalInformationAsIndefiniteLengthFlag +} + +const ( + // From RFC 8949.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 ( + tagNumSelfDescribedCBOR = 55799 + tagNumExpectedLaterEncodingBase64URL = 21 + tagNumExpectedLaterEncodingBase64 = 22 + tagNumExpectedLaterEncodingBase16 = 23 +) + +var ( + cborBreakFlag = byte(0xff) + 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 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 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 d01e26b5..fabb229b 100644 --- a/decode.go +++ b/decode.go @@ -817,9 +817,9 @@ func (opts DecOptions) validForTags(tags TagSet) error { //nolint:gocritic // ig } 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) @@ -1275,91 +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 ( - // From RFC 8949.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 isIndefiniteLength(ai byte) bool { - return ai == additionalInformationAsIndefiniteLengthFlag -} - -func isBreakFlag(raw byte) bool { - return raw == cborBreakFlag -} - -func parseInitialByte(b byte) (t cborType, ai byte) { - return getType(b), getAdditionalInformation(b) -} - -const ( - maxAdditionalInformationWithoutArgument = 23 - additionalInformationWith1ByteArgument = 24 - additionalInformationWith2ByteArgument = 25 - additionalInformationWith4ByteArgument = 26 - additionalInformationWith8ByteArgument = 27 - additionalInformationAsIndefiniteLengthFlag = 31 -) - -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 @@ -1418,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 } @@ -1601,7 +1516,7 @@ 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 { @@ -1796,7 +1711,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 } @@ -1971,7 +1886,7 @@ 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 { @@ -2093,15 +2008,15 @@ func (d *decoder) applyByteStringTextConversion( } 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 @@ -2838,7 +2753,7 @@ func (d *decoder) getHeadWithIndefiniteLengthFlag() ( indefiniteLength bool, ) { t, ai, val = d.getHead() - indefiniteLength = isIndefiniteLength(ai) + indefiniteLength = additionalInformation(ai).isIndefiniteLength() return } @@ -3127,36 +3042,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 := getType(contentHead) - 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/encode.go b/encode.go index 8882e1e9..9c79d379 100644 --- a/encode.go +++ b/encode.go @@ -418,11 +418,11 @@ func (bsm ByteSliceMode) encodingTag() (uint64, error) { 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))) } @@ -913,16 +913,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 ( - cborBreakFlag = byte(0xff) - 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 diff --git a/tag.go b/tag.go index 4fade067..4c9c99f8 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 fa7f1045..b40793b9 100644 --- a/valid.go +++ b/valid.go @@ -228,7 +228,7 @@ func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltin if t != nt { return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()} } - if isIndefiniteLength(ai) { + 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 { @@ -286,7 +286,7 @@ func (d *decoder) wellformedHeadWithIndefiniteLengthFlag() ( if err != nil { return } - indefiniteLength = isIndefiniteLength(ai) + indefiniteLength = additionalInformation(ai).isIndefiniteLength() return } @@ -363,7 +363,7 @@ func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) return t, ai, val, nil } - if isIndefiniteLength(ai) { + 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()} From 0efea34486086e679be97ced7e7401d914ec874b Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 19:30:09 -0500 Subject: [PATCH 06/10] Refactor to remove magic numbers of tag numbers --- cbor.go => common.go | 40 ++++++++++++++++++++++++++++++++-------- decode.go | 15 ++++++++++----- diagnose.go | 16 ++++++++++++---- 3 files changed, 54 insertions(+), 17 deletions(-) rename cbor.go => common.go (83%) diff --git a/cbor.go b/common.go similarity index 83% rename from cbor.go rename to common.go index 12c3e5be..f004a74b 100644 --- a/cbor.go +++ b/common.go @@ -4,7 +4,7 @@ package cbor import ( - "errors" + "fmt" "strconv" ) @@ -89,14 +89,21 @@ func parseInitialByte(b byte) (t cborType, ai byte) { } const ( + tagNumRFC3339Time = 0 + tagNumEpochTime = 1 + tagNumUnsignedBignum = 2 + tagNumNegativeBignum = 3 tagNumSelfDescribedCBOR = 55799 tagNumExpectedLaterEncodingBase64URL = 21 tagNumExpectedLaterEncodingBase64 = 22 tagNumExpectedLaterEncodingBase16 = 23 ) +const ( + cborBreakFlag = byte(0xff) +) + var ( - cborBreakFlag = byte(0xff) cborFalse = []byte{0xf4} cborTrue = []byte{0xf5} cborNil = []byte{0xf6} @@ -109,24 +116,40 @@ var ( func validBuiltinTag(tagNum uint64, contentHead byte) error { t := getType(contentHead) switch tagNum { - case 0: + case tagNumRFC3339Time: // 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 fmt.Errorf( + "cbor: tag number %d must be followed by text string, got %s", + tagNumRFC3339Time, + t.String(), + ) } return nil - case 1: + + case tagNumEpochTime: // 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 fmt.Errorf( + "cbor: tag number %d must be followed by integer or floating-point number, got %s", + tagNumEpochTime, + t.String(), + ) } return nil - case 2, 3: + + case tagNumUnsignedBignum, tagNumNegativeBignum: // 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 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 @@ -135,5 +158,6 @@ func validBuiltinTag(tagNum uint64, contentHead byte) error { // conversion. return nil } + return nil } diff --git a/decode.go b/decode.go index fabb229b..1d92ddb5 100644 --- a/decode.go +++ b/decode.go @@ -1474,7 +1474,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) @@ -1494,7 +1494,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) @@ -1516,6 +1517,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin GoType: tInfo.nonPtrType.String(), errorMsg: bi.String() + " overflows " + v.Type().String(), } + case tagNumExpectedLaterEncodingBase64URL, tagNumExpectedLaterEncodingBase64, tagNumExpectedLaterEncodingBase16: // If conversion for interoperability with text encodings is not configured, // treat tags 21-23 as unregistered tags. @@ -1834,7 +1836,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli contentOff := d.off switch tagNum { - case 0, 1: + case tagNumRFC3339Time, tagNumEpochTime: d.off = tagOff tm, _, err := d.parseToTime() if err != nil { @@ -1868,7 +1870,8 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli default: // not reachable } - case 2: + + case tagNumUnsignedBignum: b, _ := d.parseByteString() bi := new(big.Int).SetBytes(b) @@ -1876,7 +1879,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)) @@ -1886,6 +1890,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli return bi, nil } return *bi, nil + case tagNumExpectedLaterEncodingBase64URL, tagNumExpectedLaterEncodingBase64, tagNumExpectedLaterEncodingBase16: // If conversion for interoperability with text encodings is not configured, // treat tags 21-23 as unregistered tags. diff --git a/diagnose.go b/diagnose.go index cba46fff..800f25c9 100644 --- a/diagnose.go +++ b/diagnose.go @@ -398,9 +398,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 +412,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() From c5c66ba45dea9494d2416a57a087a88856c86d85 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 20:08:55 -0500 Subject: [PATCH 07/10] Refactor to remove more magic numbers --- common.go | 20 +++++++++++++++----- decode.go | 48 +++++++++++++++++++++++++++++++++--------------- diagnose.go | 26 +++++++++++++++----------- 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/common.go b/common.go index f004a74b..4e11f0f5 100644 --- a/common.go +++ b/common.go @@ -47,11 +47,21 @@ func (t cborType) String() string { type additionalInformation uint8 const ( - maxAdditionalInformationWithoutArgument = 23 - additionalInformationWith1ByteArgument = 24 - additionalInformationWith2ByteArgument = 25 - additionalInformationWith4ByteArgument = 26 - additionalInformationWith8ByteArgument = 27 + maxAdditionalInformationWithoutArgument = 23 + additionalInformationWith1ByteArgument = 24 + additionalInformationWith2ByteArgument = 25 + additionalInformationWith4ByteArgument = 26 + additionalInformationWith8ByteArgument = 27 + + // additional information with major type 7 + additionalInformationAsFalse = 20 + additionalInformationAsTrue = 21 + additionalInformationAsNull = 22 + additionalInformationAsUndefined = 23 + additionalInformationAsFloat16 = 25 + additionalInformationAsFloat32 = 26 + additionalInformationAsFloat64 = 27 + additionalInformationAsIndefiniteLengthFlag = 31 ) diff --git a/decode.go b/decode.go index 1d92ddb5..ea943977 100644 --- a/decode.go +++ b/decode.go @@ -1444,15 +1444,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{ @@ -1462,10 +1465,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) } @@ -1664,12 +1671,15 @@ func (d *decoder) parseToTime() (time.Time, bool, error) { _, 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()} } @@ -1931,6 +1941,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)] { @@ -1943,20 +1954,27 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli 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: diff --git a/diagnose.go b/diagnose.go index 800f25c9..05850f1a 100644 --- a/diagnose.go +++ b/diagnose.go @@ -441,23 +441,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: @@ -628,7 +630,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(): @@ -644,7 +646,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: @@ -660,7 +662,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: @@ -703,13 +705,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 } From fdf5bd837811dc55be58ae31843f0d4d0d40511a Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 21:14:04 -0500 Subject: [PATCH 08/10] Refactor to remove more magic numbers --- common.go | 14 ++++++++++++-- decode.go | 36 +++++++++++++++++++++++++++++++++++ diagnose.go | 18 ++++++++++-------- encode.go | 37 +++++++++++++++++++++++++++++------- encode_test.go | 51 ++++++++++++++++++++++++++++++++++---------------- simplevalue.go | 8 ++++---- stream.go | 8 ++++---- 7 files changed, 131 insertions(+), 41 deletions(-) diff --git a/common.go b/common.go index 4e11f0f5..2793ba3f 100644 --- a/common.go +++ b/common.go @@ -53,7 +53,7 @@ const ( additionalInformationWith4ByteArgument = 26 additionalInformationWith8ByteArgument = 27 - // additional information with major type 7 + // For major type 7. additionalInformationAsFalse = 20 additionalInformationAsTrue = 21 additionalInformationAsNull = 22 @@ -62,9 +62,15 @@ const ( 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 } @@ -110,7 +116,11 @@ const ( ) const ( - cborBreakFlag = byte(0xff) + cborBreakFlag = byte(0xff) + cborByteStringWithIndefiniteLengthHead = byte(0x5f) + cborTextStringWithIndefiniteLengthHead = byte(0x7f) + cborArrayWithIndefiniteLengthHead = byte(0x9f) + cborMapWithIndefiniteLengthHead = byte(0xbf) ) var ( diff --git a/decode.go b/decode.go index ea943977..5449d533 100644 --- a/decode.go +++ b/decode.go @@ -1358,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. @@ -1374,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) } @@ -1535,6 +1538,7 @@ func (d *decoder) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolin }() } } + return d.parseToValue(v, tInfo) case cborTypeArray: @@ -1628,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 { @@ -1638,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 { @@ -1648,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 { @@ -1667,6 +1674,7 @@ 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 @@ -1690,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()} } @@ -1822,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 @@ -1834,12 +1845,14 @@ 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() @@ -1852,9 +1865,11 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli if err != nil { return nil, err } + switch d.dm.timeTagToAny { case TimeTagToTime: return tm, nil + case TimeTagToRFC3339: if tagNum == 1 { tm = tm.UTC() @@ -1866,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() @@ -1877,6 +1893,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli return nil, err } return string(text), nil + default: // not reachable } @@ -1953,6 +1970,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli if ai < 20 || ai == 24 { return SimpleValue(val), nil } + switch ai { case additionalInformationAsFalse, additionalInformationAsTrue: @@ -1977,6 +1995,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli case cborTypeArray: return d.parseArray() + case cborTypeMap: if d.dm.defaultMapType != nil { m := reflect.New(d.dm.defaultMapType) @@ -1988,6 +2007,7 @@ func (d *decoder) parse(skipSelfDescribedTag bool) (interface{}, error) { //noli } return d.parseMap() } + return nil, nil } @@ -2035,19 +2055,23 @@ func (d *decoder) applyByteStringTextConversion( encoded := make([]byte, base64.RawURLEncoding.EncodedLen(len(src))) base64.RawURLEncoding.Encode(encoded, src) return encoded, true, nil + case tagNumExpectedLaterEncodingBase64: encoded := make([]byte, base64.StdEncoding.EncodedLen(len(src))) base64.StdEncoding.Encode(encoded, src) return encoded, true, nil + 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 @@ -2064,6 +2088,7 @@ func (d *decoder) applyByteStringTextConversion( 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) @@ -2071,6 +2096,7 @@ func (d *decoder) applyByteStringTextConversion( 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) @@ -2756,14 +2782,17 @@ 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() } @@ -2893,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{ @@ -2903,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)) @@ -2928,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) @@ -3026,6 +3059,7 @@ func isImmutableKind(k reflect.Kind) bool { reflect.Float32, reflect.Float64, reflect.String: return true + default: return false } @@ -3035,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: @@ -3057,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 { diff --git a/diagnose.go b/diagnose.go index 05850f1a..2794df3f 100644 --- a/diagnose.go +++ b/diagnose.go @@ -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 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("{_ ") @@ -573,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 { @@ -678,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 diff --git a/encode.go b/encode.go index 9c79d379..25dbf6e9 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,10 +418,13 @@ func (bsm ByteSliceMode) encodingTag() (uint64, error) { switch bsm { case ByteSliceToByteString: return 0, nil + case ByteSliceToByteStringWithExpectedConversionToBase64URL: return tagNumExpectedLaterEncodingBase64URL, nil + case ByteSliceToByteStringWithExpectedConversionToBase64: return tagNumExpectedLaterEncodingBase64, nil + case ByteSliceToByteStringWithExpectedConversionToBase16: return tagNumExpectedLaterEncodingBase16, nil } @@ -978,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 } @@ -1002,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 @@ -1012,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 @@ -1023,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) @@ -1100,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 @@ -1108,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 @@ -1116,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 @@ -1478,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()) @@ -1490,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)) @@ -1690,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 } @@ -1718,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 { @@ -1754,6 +1776,7 @@ func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) { } } return encodeStruct, isEmptyStruct + case reflect.Interface: return encodeIntf, isEmptyIntf } diff --git a/encode_test.go b/encode_test.go index 463bef88..4b7cc826 100644 --- a/encode_test.go +++ b/encode_test.go @@ -449,27 +449,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) { diff --git a/simplevalue.go b/simplevalue.go index 6f93f67c..de175cee 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 dcb60b44..507ab6c1 100644 --- a/stream.go +++ b/stream.go @@ -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 { From b426c3e8c69047ae7fa945d71de9f5a5232bf17b Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 21:21:12 -0500 Subject: [PATCH 09/10] Lint --- encode.go | 6 +++--- encode_test.go | 6 ++++-- stream_test.go | 3 ++- structfields.go | 6 +++--- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/encode.go b/encode.go index 25dbf6e9..90a1a59d 100644 --- a/encode.go +++ b/encode.go @@ -1347,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 @@ -1395,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 }) @@ -1870,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/encode_test.go b/encode_test.go index 4b7cc826..2cc88374 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 { diff --git a/stream_test.go b/stream_test.go index 06194b28..8e732e93 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/structfields.go b/structfields.go index d2a2c1d1..81228acf 100644 --- a/structfields.go +++ b/structfields.go @@ -173,12 +173,12 @@ func appendFields( 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 { @@ -207,7 +207,7 @@ func appendFields( 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, From 7ed3916635d12a94551412da27188d6915328ee5 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Mon, 27 May 2024 22:04:31 -0500 Subject: [PATCH 10/10] Add comment --- common.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common.go b/common.go index 2793ba3f..0387db60 100644 --- a/common.go +++ b/common.go @@ -76,10 +76,10 @@ func (ai additionalInformation) isIndefiniteLength() bool { } const ( - // From RFC 8949.3: - // The initial byte of each encoded data item contains both information about the major type + // 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). + // (the low-order 5 bits)." // typeMask is used to extract major type in initial byte of encoded data item. typeMask = 0xe0 @@ -109,10 +109,10 @@ const ( tagNumEpochTime = 1 tagNumUnsignedBignum = 2 tagNumNegativeBignum = 3 - tagNumSelfDescribedCBOR = 55799 tagNumExpectedLaterEncodingBase64URL = 21 tagNumExpectedLaterEncodingBase64 = 22 tagNumExpectedLaterEncodingBase16 = 23 + tagNumSelfDescribedCBOR = 55799 ) const (