From ad9dc1c1617aa42398f67d497b206be6c5249625 Mon Sep 17 00:00:00 2001 From: Faye Amacker <33205765+fxamacker@users.noreply.github.com> Date: Sat, 4 Nov 2023 19:44:51 -0500 Subject: [PATCH] Make valid() reject negative values for modes For modes of integer type, make valid() reject negative values since 0 is default and non-negative values are used. Also reformat comments with gofmt. --- decode.go | 14 +- decode_test.go | 210 ++++++++++++++++----- doc.go | 82 ++++----- encode.go | 96 +++++----- encode_test.go | 490 +++++++++++++++++++++++++++++++++---------------- tag.go | 4 +- 6 files changed, 599 insertions(+), 297 deletions(-) diff --git a/decode.go b/decode.go index 65be7850..bc41565a 100644 --- a/decode.go +++ b/decode.go @@ -226,7 +226,7 @@ const ( ) func (dmkm DupMapKeyMode) valid() bool { - return dmkm < maxDupMapKeyMode + return dmkm >= 0 && dmkm < maxDupMapKeyMode } // IndefLengthMode specifies whether to allow indefinite length items. @@ -243,7 +243,7 @@ const ( ) func (m IndefLengthMode) valid() bool { - return m < maxIndefLengthMode + return m >= 0 && m < maxIndefLengthMode } // TagsMode specifies whether to allow CBOR tags. @@ -260,7 +260,7 @@ const ( ) func (tm TagsMode) valid() bool { - return tm < maxTagsMode + return tm >= 0 && tm < maxTagsMode } // IntDecMode specifies which Go int type (int64 or uint64) should @@ -282,7 +282,7 @@ const ( ) func (idm IntDecMode) valid() bool { - return idm < maxIntDec + return idm >= 0 && idm < maxIntDec } // MapKeyByteStringMode specifies how to decode CBOR byte string (major type 2) @@ -312,7 +312,7 @@ const ( ) func (mkbsm MapKeyByteStringMode) valid() bool { - return mkbsm < maxMapKeyByteStringMode + return mkbsm >= 0 && mkbsm < maxMapKeyByteStringMode } // ExtraDecErrorCond specifies extra conditions that should be treated as errors. @@ -350,7 +350,7 @@ const ( ) func (um UTF8Mode) valid() bool { - return um < maxUTF8Mode + return um >= 0 && um < maxUTF8Mode } // FieldNameMatchingMode specifies how string keys in CBOR maps are matched to Go struct field names. @@ -1985,7 +1985,7 @@ var ( typeBinaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() ) -func fillNil(t cborType, v reflect.Value) error { +func fillNil(_ cborType, v reflect.Value) error { switch v.Kind() { case reflect.Slice, reflect.Map, reflect.Interface, reflect.Ptr: v.Set(reflect.Zero(v.Type())) diff --git a/decode_test.go b/decode_test.go index e9dc84d3..aa409a3d 100644 --- a/decode_test.go +++ b/decode_test.go @@ -3294,22 +3294,58 @@ func testRoundTrip(t *testing.T, testCases []roundTripTest, em EncMode, dm DecMo } func TestDecModeInvalidTimeTag(t *testing.T) { - wantErrorMsg := "cbor: invalid TimeTag 101" - _, err := DecOptions{TimeTag: 101}.DecMode() - if err == nil { - t.Errorf("DecMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: DecOptions{TimeTag: -1}, + wantErrorMsg: "cbor: invalid TimeTag -1", + }, + { + name: "above range of valid modes", + opts: DecOptions{TimeTag: 101}, + wantErrorMsg: "cbor: invalid TimeTag 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } func TestDecModeInvalidDuplicateMapKey(t *testing.T) { - wantErrorMsg := "cbor: invalid DupMapKey 101" - _, err := DecOptions{DupMapKey: 101}.DecMode() - if err == nil { - t.Errorf("DecMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: DecOptions{DupMapKey: -1}, + wantErrorMsg: "cbor: invalid DupMapKey -1", + }, + { + name: "above range of valid modes", + opts: DecOptions{DupMapKey: 101}, + wantErrorMsg: "cbor: invalid DupMapKey 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -3437,22 +3473,58 @@ func TestDecModeInvalidMaxArrayElements(t *testing.T) { } func TestDecModeInvalidIndefiniteLengthMode(t *testing.T) { - wantErrorMsg := "cbor: invalid IndefLength 101" - _, err := DecOptions{IndefLength: 101}.DecMode() - if err == nil { - t.Errorf("DecMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: DecOptions{IndefLength: -1}, + wantErrorMsg: "cbor: invalid IndefLength -1", + }, + { + name: "above range of valid modes", + opts: DecOptions{IndefLength: 101}, + wantErrorMsg: "cbor: invalid IndefLength 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } func TestDecModeInvalidTagsMode(t *testing.T) { - wantErrorMsg := "cbor: invalid TagsMd 101" - _, err := DecOptions{TagsMd: 101}.DecMode() - if err == nil { - t.Errorf("DecMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: DecOptions{TagsMd: -1}, + wantErrorMsg: "cbor: invalid TagsMd -1", + }, + { + name: "above range of valid modes", + opts: DecOptions{TagsMd: 101}, + wantErrorMsg: "cbor: invalid TagsMd 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -4605,12 +4677,30 @@ func TestDecTagsMdOption(t *testing.T) { } func TestDecModeInvalidIntDec(t *testing.T) { - wantErrorMsg := "cbor: invalid IntDec 101" - _, err := DecOptions{IntDec: 101}.DecMode() - if err == nil { - t.Errorf("DecMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: DecOptions{IntDec: -1}, + wantErrorMsg: "cbor: invalid IntDec -1", + }, + { + name: "above range of valid modes", + opts: DecOptions{IntDec: 101}, + wantErrorMsg: "cbor: invalid IntDec 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -4664,12 +4754,30 @@ func TestIntDec(t *testing.T) { } func TestDecModeInvalidMapKeyByteString(t *testing.T) { - wantErrorMsg := "cbor: invalid MapKeyByteString 101" - _, err := DecOptions{MapKeyByteString: 101}.DecMode() - if err == nil { - t.Errorf("DecMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: DecOptions{MapKeyByteString: -1}, + wantErrorMsg: "cbor: invalid MapKeyByteString -1", + }, + { + name: "above range of valid modes", + opts: DecOptions{MapKeyByteString: 101}, + wantErrorMsg: "cbor: invalid MapKeyByteString 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -4844,12 +4952,30 @@ func TestExtraErrorCondUnknownField(t *testing.T) { } func TestInvalidUTF8Mode(t *testing.T) { - wantErrorMsg := "cbor: invalid UTF8 2" - _, err := DecOptions{UTF8: 2}.DecMode() - if err == nil { - t.Errorf("DecMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts DecOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: DecOptions{UTF8: -1}, + wantErrorMsg: "cbor: invalid UTF8 -1", + }, + { + name: "above range of valid modes", + opts: DecOptions{UTF8: 101}, + wantErrorMsg: "cbor: invalid UTF8 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.DecMode() + if err == nil { + t.Errorf("DecMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("DecMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } diff --git a/doc.go b/doc.go index b7e377f3..23f68b98 100644 --- a/doc.go +++ b/doc.go @@ -17,17 +17,17 @@ For example, "toarray" tag makes struct fields encode to CBOR array elements. A Latest docs can be viewed at https://github.com/fxamacker/cbor#cbor-library-in-go -Basics +# Basics The Quick Start guide is at https://github.com/fxamacker/cbor#quick-start Function signatures identical to encoding/json include: - Marshal, Unmarshal, NewEncoder, NewDecoder, (*Encoder).Encode, (*Decoder).Decode. + Marshal, Unmarshal, NewEncoder, NewDecoder, (*Encoder).Encode, (*Decoder).Decode. Standard interfaces include: - BinaryMarshaler, BinaryUnmarshaler, Marshaler, and Unmarshaler. + BinaryMarshaler, BinaryUnmarshaler, Marshaler, and Unmarshaler. Custom encoding and decoding is possible by implementing standard interfaces for user-defined Go types. @@ -39,9 +39,9 @@ creating modes from options at runtime. EncMode and DecMode interfaces are created from EncOptions or DecOptions structs. - em, err := cbor.EncOptions{...}.EncMode() - em, err := cbor.CanonicalEncOptions().EncMode() - em, err := cbor.CTAP2EncOptions().EncMode() + em, err := cbor.EncOptions{...}.EncMode() + em, err := cbor.CanonicalEncOptions().EncMode() + em, err := cbor.CTAP2EncOptions().EncMode() Modes use immutable options to avoid side-effects and simplify concurrency. Behavior of modes won't accidentally change at runtime after they're created. @@ -50,55 +50,55 @@ Modes are intended to be reused and are safe for concurrent use. EncMode and DecMode Interfaces - // EncMode interface uses immutable options and is safe for concurrent use. - type EncMode interface { - Marshal(v interface{}) ([]byte, error) - NewEncoder(w io.Writer) *Encoder - EncOptions() EncOptions // returns copy of options - } + // EncMode interface uses immutable options and is safe for concurrent use. + type EncMode interface { + Marshal(v interface{}) ([]byte, error) + NewEncoder(w io.Writer) *Encoder + EncOptions() EncOptions // returns copy of options + } - // DecMode interface uses immutable options and is safe for concurrent use. - type DecMode interface { - Unmarshal(data []byte, v interface{}) error - NewDecoder(r io.Reader) *Decoder - DecOptions() DecOptions // returns copy of options - } + // DecMode interface uses immutable options and is safe for concurrent use. + type DecMode interface { + Unmarshal(data []byte, v interface{}) error + NewDecoder(r io.Reader) *Decoder + DecOptions() DecOptions // returns copy of options + } Using Default Encoding Mode - b, err := cbor.Marshal(v) + b, err := cbor.Marshal(v) - encoder := cbor.NewEncoder(w) - err = encoder.Encode(v) + encoder := cbor.NewEncoder(w) + err = encoder.Encode(v) Using Default Decoding Mode - err := cbor.Unmarshal(b, &v) + err := cbor.Unmarshal(b, &v) - decoder := cbor.NewDecoder(r) - err = decoder.Decode(&v) + decoder := cbor.NewDecoder(r) + err = decoder.Decode(&v) Creating and Using Encoding Modes - // Create EncOptions using either struct literal or a function. - opts := cbor.CanonicalEncOptions() + // Create EncOptions using either struct literal or a function. + opts := cbor.CanonicalEncOptions() - // If needed, modify encoding options - opts.Time = cbor.TimeUnix + // If needed, modify encoding options + opts.Time = cbor.TimeUnix - // Create reusable EncMode interface with immutable options, safe for concurrent use. - em, err := opts.EncMode() + // Create reusable EncMode interface with immutable options, safe for concurrent use. + em, err := opts.EncMode() - // Use EncMode like encoding/json, with same function signatures. - b, err := em.Marshal(v) - // or - encoder := em.NewEncoder(w) - err := encoder.Encode(v) + // Use EncMode like encoding/json, with same function signatures. + b, err := em.Marshal(v) + // or + encoder := em.NewEncoder(w) + err := encoder.Encode(v) - // NOTE: Both em.Marshal(v) and encoder.Encode(v) use encoding options - // specified during creation of em (encoding mode). + // NOTE: Both em.Marshal(v) and encoder.Encode(v) use encoding options + // specified during creation of em (encoding mode). -CBOR Options +# CBOR Options Predefined Encoding Options: https://github.com/fxamacker/cbor#predefined-encoding-options @@ -106,7 +106,7 @@ Encoding Options: https://github.com/fxamacker/cbor#encoding-options Decoding Options: https://github.com/fxamacker/cbor#decoding-options -Struct Tags +# Struct Tags Struct tags like `cbor:"name,omitempty"` and `json:"name,omitempty"` work as expected. If both struct tags are specified then `cbor` is used. @@ -121,9 +121,9 @@ https://raw.githubusercontent.com/fxamacker/images/master/cbor/v2.0.0/cbor_easy_ Struct tags are listed at https://github.com/fxamacker/cbor#struct-tags-1 -Tests and Fuzzing +# Tests and Fuzzing Over 375 tests are included in this package. Cover-guided fuzzing is handled by -fxamacker/cbor-fuzz. +a private fuzzer that replaced fxamacker/cbor-fuzz years ago. */ package cbor diff --git a/encode.go b/encode.go index 10255a60..afb6c8c7 100644 --- a/encode.go +++ b/encode.go @@ -143,7 +143,7 @@ const ( ) func (sm SortMode) valid() bool { - return sm < maxSortMode + return sm >= 0 && sm < maxSortMode } // ShortestFloatMode specifies which floating-point format should @@ -168,7 +168,7 @@ const ( ) func (sfm ShortestFloatMode) valid() bool { - return sfm < maxShortestFloat + return sfm >= 0 && sfm < maxShortestFloat } // NaNConvertMode specifies how to encode NaN and overrides ShortestFloatMode. @@ -196,7 +196,7 @@ const ( ) func (ncm NaNConvertMode) valid() bool { - return ncm < maxNaNConvert + return ncm >= 0 && ncm < maxNaNConvert } // InfConvertMode specifies how to encode Infinity and overrides ShortestFloatMode. @@ -214,7 +214,7 @@ const ( ) func (icm InfConvertMode) valid() bool { - return icm < maxInfConvert + return icm >= 0 && icm < maxInfConvert } // TimeMode specifies how to encode time.Time values. @@ -241,7 +241,7 @@ const ( ) func (tm TimeMode) valid() bool { - return tm < maxTimeMode + return tm >= 0 && tm < maxTimeMode } // BigIntConvertMode specifies how to encode big.Int values. @@ -261,7 +261,7 @@ const ( ) func (bim BigIntConvertMode) valid() bool { - return bim < maxBigIntConvert + return bim >= 0 && bim < maxBigIntConvert } // NilContainersMode specifies how to encode nil slices and maps. @@ -280,7 +280,7 @@ const ( ) func (m NilContainersMode) valid() bool { - return m < maxNilContainersMode + return m >= 0 && m < maxNilContainersMode } // EncOptions specifies encoding options. @@ -321,19 +321,18 @@ type EncOptions struct { // CanonicalEncOptions returns EncOptions for "Canonical CBOR" encoding, // defined in RFC 7049 Section 3.9 with the following rules: // -// 1. "Integers must be as small as possible." -// 2. "The expression of lengths in major types 2 through 5 must be as short as possible." -// 3. The keys in every map must be sorted in length-first sorting order. -// See SortLengthFirst for details. -// 4. "Indefinite-length items must be made into definite-length items." -// 5. "If a protocol allows for IEEE floats, then additional canonicalization rules might -// need to be added. One example rule might be to have all floats start as a 64-bit -// float, then do a test conversion to a 32-bit float; if the result is the same numeric -// value, use the shorter value and repeat the process with a test conversion to a -// 16-bit float. (This rule selects 16-bit float for positive and negative Infinity -// as well.) Also, there are many representations for NaN. If NaN is an allowed value, -// it must always be represented as 0xf97e00." -// +// 1. "Integers must be as small as possible." +// 2. "The expression of lengths in major types 2 through 5 must be as short as possible." +// 3. The keys in every map must be sorted in length-first sorting order. +// See SortLengthFirst for details. +// 4. "Indefinite-length items must be made into definite-length items." +// 5. "If a protocol allows for IEEE floats, then additional canonicalization rules might +// need to be added. One example rule might be to have all floats start as a 64-bit +// float, then do a test conversion to a 32-bit float; if the result is the same numeric +// value, use the shorter value and repeat the process with a test conversion to a +// 16-bit float. (This rule selects 16-bit float for positive and negative Infinity +// as well.) Also, there are many representations for NaN. If NaN is an allowed value, +// it must always be represented as 0xf97e00." func CanonicalEncOptions() EncOptions { return EncOptions{ Sort: SortCanonical, @@ -347,14 +346,13 @@ func CanonicalEncOptions() EncOptions { // CTAP2EncOptions returns EncOptions for "CTAP2 Canonical CBOR" encoding, // defined in CTAP specification, with the following rules: // -// 1. "Integers must be encoded as small as possible." -// 2. "The representations of any floating-point values are not changed." -// 3. "The expression of lengths in major types 2 through 5 must be as short as possible." -// 4. "Indefinite-length items must be made into definite-length items."" -// 5. The keys in every map must be sorted in bytewise lexicographic order. -// See SortBytewiseLexical for details. -// 6. "Tags as defined in Section 2.4 in [RFC7049] MUST NOT be present." -// +// 1. "Integers must be encoded as small as possible." +// 2. "The representations of any floating-point values are not changed." +// 3. "The expression of lengths in major types 2 through 5 must be as short as possible." +// 4. "Indefinite-length items must be made into definite-length items."" +// 5. The keys in every map must be sorted in bytewise lexicographic order. +// See SortBytewiseLexical for details. +// 6. "Tags as defined in Section 2.4 in [RFC7049] MUST NOT be present." func CTAP2EncOptions() EncOptions { return EncOptions{ Sort: SortCTAP2, @@ -369,14 +367,13 @@ func CTAP2EncOptions() EncOptions { // CoreDetEncOptions returns EncOptions for "Core Deterministic" encoding, // defined in RFC 7049bis with the following rules: // -// 1. "Preferred serialization MUST be used. In particular, this means that arguments -// (see Section 3) for integers, lengths in major types 2 through 5, and tags MUST -// be as short as possible" -// "Floating point values also MUST use the shortest form that preserves the value" -// 2. "Indefinite-length items MUST NOT appear." -// 3. "The keys in every map MUST be sorted in the bytewise lexicographic order of -// their deterministic encodings." -// +// 1. "Preferred serialization MUST be used. In particular, this means that arguments +// (see Section 3) for integers, lengths in major types 2 through 5, and tags MUST +// be as short as possible" +// "Floating point values also MUST use the shortest form that preserves the value" +// 2. "Indefinite-length items MUST NOT appear." +// 3. "The keys in every map MUST be sorted in the bytewise lexicographic order of +// their deterministic encodings." func CoreDetEncOptions() EncOptions { return EncOptions{ Sort: SortCoreDeterministic, @@ -390,19 +387,18 @@ func CoreDetEncOptions() EncOptions { // PreferredUnsortedEncOptions returns EncOptions for "Preferred Serialization" encoding, // defined in RFC 7049bis with the following rules: // -// 1. "The preferred serialization always uses the shortest form of representing the argument -// (Section 3);" -// 2. "it also uses the shortest floating-point encoding that preserves the value being -// encoded (see Section 5.5)." -// "The preferred encoding for a floating-point value is the shortest floating-point encoding -// that preserves its value, e.g., 0xf94580 for the number 5.5, and 0xfa45ad9c00 for the -// number 5555.5, unless the CBOR-based protocol specifically excludes the use of the shorter -// floating-point encodings. For NaN values, a shorter encoding is preferred if zero-padding -// the shorter significand towards the right reconstitutes the original NaN value (for many -// applications, the single NaN encoding 0xf97e00 will suffice)." -// 3. "Definite length encoding is preferred whenever the length is known at the time the -// serialization of the item starts." -// +// 1. "The preferred serialization always uses the shortest form of representing the argument +// (Section 3);" +// 2. "it also uses the shortest floating-point encoding that preserves the value being +// encoded (see Section 5.5)." +// "The preferred encoding for a floating-point value is the shortest floating-point encoding +// that preserves its value, e.g., 0xf94580 for the number 5.5, and 0xfa45ad9c00 for the +// number 5555.5, unless the CBOR-based protocol specifically excludes the use of the shorter +// floating-point encodings. For NaN values, a shorter encoding is preferred if zero-padding +// the shorter significand towards the right reconstitutes the original NaN value (for many +// applications, the single NaN encoding 0xf97e00 will suffice)." +// 3. "Definite length encoding is preferred whenever the length is known at the time the +// serialization of the item starts." func PreferredUnsortedEncOptions() EncOptions { return EncOptions{ Sort: SortNone, @@ -1409,7 +1405,7 @@ func getEncodeIndirectValueFunc(t reflect.Type) encodeFunc { } } -func alwaysNotEmpty(v reflect.Value) (empty bool, err error) { +func alwaysNotEmpty(_ reflect.Value) (empty bool, err error) { return false, nil } diff --git a/encode_test.go b/encode_test.go index 15b4c6fb..e5d22e08 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1463,7 +1463,7 @@ type TReader struct { X int } -func (s TReader) Read(p []byte) (n int, err error) { +func (s TReader) Read(_ []byte) (n int, err error) { return 0, nil } @@ -1864,12 +1864,30 @@ func parseTime(layout string, value string) time.Time { } func TestInvalidTimeMode(t *testing.T) { - wantErrorMsg := "cbor: invalid TimeMode 100" - _, err := EncOptions{Time: TimeMode(100)}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{Time: -1}, + wantErrorMsg: "cbor: invalid TimeMode -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{Time: 101}, + wantErrorMsg: "cbor: invalid TimeMode 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -2330,12 +2348,30 @@ func TestStructSort(t *testing.T) { } func TestInvalidSort(t *testing.T) { - wantErrorMsg := "cbor: invalid SortMode 100" - _, err := EncOptions{Sort: SortMode(100)}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{Sort: -1}, + wantErrorMsg: "cbor: invalid SortMode -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{Sort: 101}, + wantErrorMsg: "cbor: invalid SortMode 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -2635,103 +2671,103 @@ func TestShortestFloat16(t *testing.T) { } /* -func TestShortestFloat32(t *testing.T) { - testCases := []struct { - name string - f64 float64 - wantCborData []byte - }{ - // Data from RFC 7049 appendix A - {"Shrink to float32", 0.0, hexDecode("fa00000000")}, - {"Shrink to float32", 1.0, hexDecode("fa3f800000")}, - {"Shrink to float32", 1.5, hexDecode("fa3fc00000")}, - {"Shrink to float32", 65504.0, hexDecode("fa477fe000")}, - {"Shrink to float32", 5.960464477539063e-08, hexDecode("fa33800000")}, - {"Shrink to float32", 6.103515625e-05, hexDecode("fa38800000")}, - {"Shrink to float32", -4.0, hexDecode("fac0800000")}, - // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format - {"Shrink to float32", 0.333251953125, hexDecode("fa3eaaa000")}, - // Data from 7049bis 4.2.1 and 5.5 - {"Shrink to float32", 5.5, hexDecode("fa40b00000")}, - // Data from RFC 7049 appendix A - {"Shrink to float32", 100000.0, hexDecode("fa47c35000")}, - {"Shrink to float32", 3.4028234663852886e+38, hexDecode("fa7f7fffff")}, - // Data from 7049bis 4.2.1 and 5.5 - {"Shrink to float32", 5555.5, hexDecode("fa45ad9c00")}, - {"Shrink to float32", 1000000.5, hexDecode("fa49742408")}, - // Data from RFC 7049 appendix A - {"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")}, - } - em, err := EncOptions{ShortestFloat: ShortestFloat32}.EncMode() - if err != nil { - t.Errorf("EncMode() returned an error %v", err) - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - b, err := em.Marshal(tc.f64) - if err != nil { - t.Errorf("Marshal(%v) returned error %v", tc.f64, err) - } else if !bytes.Equal(b, tc.wantCborData) { - t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData) - } - var f64 float64 - if err = Unmarshal(b, &f64); err != nil { - t.Errorf("Unmarshal(0x%x) returned error %v", b, err) - } else if f64 != tc.f64 { - t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64) - } - }) + func TestShortestFloat32(t *testing.T) { + testCases := []struct { + name string + f64 float64 + wantCborData []byte + }{ + // Data from RFC 7049 appendix A + {"Shrink to float32", 0.0, hexDecode("fa00000000")}, + {"Shrink to float32", 1.0, hexDecode("fa3f800000")}, + {"Shrink to float32", 1.5, hexDecode("fa3fc00000")}, + {"Shrink to float32", 65504.0, hexDecode("fa477fe000")}, + {"Shrink to float32", 5.960464477539063e-08, hexDecode("fa33800000")}, + {"Shrink to float32", 6.103515625e-05, hexDecode("fa38800000")}, + {"Shrink to float32", -4.0, hexDecode("fac0800000")}, + // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format + {"Shrink to float32", 0.333251953125, hexDecode("fa3eaaa000")}, + // Data from 7049bis 4.2.1 and 5.5 + {"Shrink to float32", 5.5, hexDecode("fa40b00000")}, + // Data from RFC 7049 appendix A + {"Shrink to float32", 100000.0, hexDecode("fa47c35000")}, + {"Shrink to float32", 3.4028234663852886e+38, hexDecode("fa7f7fffff")}, + // Data from 7049bis 4.2.1 and 5.5 + {"Shrink to float32", 5555.5, hexDecode("fa45ad9c00")}, + {"Shrink to float32", 1000000.5, hexDecode("fa49742408")}, + // Data from RFC 7049 appendix A + {"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")}, + } + em, err := EncOptions{ShortestFloat: ShortestFloat32}.EncMode() + if err != nil { + t.Errorf("EncMode() returned an error %v", err) + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + b, err := em.Marshal(tc.f64) + if err != nil { + t.Errorf("Marshal(%v) returned error %v", tc.f64, err) + } else if !bytes.Equal(b, tc.wantCborData) { + t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData) + } + var f64 float64 + if err = Unmarshal(b, &f64); err != nil { + t.Errorf("Unmarshal(0x%x) returned error %v", b, err) + } else if f64 != tc.f64 { + t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64) + } + }) + } } -} -func TestShortestFloat64(t *testing.T) { - testCases := []struct { - name string - f64 float64 - wantCborData []byte - }{ - // Data from RFC 7049 appendix A - {"Shrink to float64", 0.0, hexDecode("fb0000000000000000")}, - {"Shrink to float64", 1.0, hexDecode("fb3ff0000000000000")}, - {"Shrink to float64", 1.5, hexDecode("fb3ff8000000000000")}, - {"Shrink to float64", 65504.0, hexDecode("fb40effc0000000000")}, - {"Shrink to float64", 5.960464477539063e-08, hexDecode("fb3e70000000000000")}, - {"Shrink to float64", 6.103515625e-05, hexDecode("fb3f10000000000000")}, - {"Shrink to float64", -4.0, hexDecode("fbc010000000000000")}, - // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format - {"Shrink to float64", 0.333251953125, hexDecode("fb3fd5540000000000")}, - // Data from 7049bis 4.2.1 and 5.5 - {"Shrink to float64", 5.5, hexDecode("fb4016000000000000")}, - // Data from RFC 7049 appendix A - {"Shrink to float64", 100000.0, hexDecode("fb40f86a0000000000")}, - {"Shrink to float64", 3.4028234663852886e+38, hexDecode("fb47efffffe0000000")}, - // Data from 7049bis 4.2.1 and 5.5 - {"Shrink to float64", 5555.5, hexDecode("fb40b5b38000000000")}, - {"Shrink to float64", 1000000.5, hexDecode("fb412e848100000000")}, - // Data from RFC 7049 appendix A - {"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")}, - } - em, err := EncOptions{ShortestFloat: ShortestFloat64}.EncMode() - if err != nil { - t.Errorf("EncMode() returned an error %v", err) - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - b, err := em.Marshal(tc.f64) - if err != nil { - t.Errorf("Marshal(%v) returned error %v", tc.f64, err) - } else if !bytes.Equal(b, tc.wantCborData) { - t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData) - } - var f64 float64 - if err = Unmarshal(b, &f64); err != nil { - t.Errorf("Unmarshal(0x%x) returned error %v", b, err) - } else if f64 != tc.f64 { - t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64) - } - }) + func TestShortestFloat64(t *testing.T) { + testCases := []struct { + name string + f64 float64 + wantCborData []byte + }{ + // Data from RFC 7049 appendix A + {"Shrink to float64", 0.0, hexDecode("fb0000000000000000")}, + {"Shrink to float64", 1.0, hexDecode("fb3ff0000000000000")}, + {"Shrink to float64", 1.5, hexDecode("fb3ff8000000000000")}, + {"Shrink to float64", 65504.0, hexDecode("fb40effc0000000000")}, + {"Shrink to float64", 5.960464477539063e-08, hexDecode("fb3e70000000000000")}, + {"Shrink to float64", 6.103515625e-05, hexDecode("fb3f10000000000000")}, + {"Shrink to float64", -4.0, hexDecode("fbc010000000000000")}, + // Data from https://en.wikipedia.org/wiki/Half-precision_floating-point_format + {"Shrink to float64", 0.333251953125, hexDecode("fb3fd5540000000000")}, + // Data from 7049bis 4.2.1 and 5.5 + {"Shrink to float64", 5.5, hexDecode("fb4016000000000000")}, + // Data from RFC 7049 appendix A + {"Shrink to float64", 100000.0, hexDecode("fb40f86a0000000000")}, + {"Shrink to float64", 3.4028234663852886e+38, hexDecode("fb47efffffe0000000")}, + // Data from 7049bis 4.2.1 and 5.5 + {"Shrink to float64", 5555.5, hexDecode("fb40b5b38000000000")}, + {"Shrink to float64", 1000000.5, hexDecode("fb412e848100000000")}, + // Data from RFC 7049 appendix A + {"Shrink to float64", 1.0e+300, hexDecode("fb7e37e43c8800759c")}, + } + em, err := EncOptions{ShortestFloat: ShortestFloat64}.EncMode() + if err != nil { + t.Errorf("EncMode() returned an error %v", err) + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + b, err := em.Marshal(tc.f64) + if err != nil { + t.Errorf("Marshal(%v) returned error %v", tc.f64, err) + } else if !bytes.Equal(b, tc.wantCborData) { + t.Errorf("Marshal(%v) = 0x%x, want 0x%x", tc.f64, b, tc.wantCborData) + } + var f64 float64 + if err = Unmarshal(b, &f64); err != nil { + t.Errorf("Unmarshal(0x%x) returned error %v", b, err) + } else if f64 != tc.f64 { + t.Errorf("Unmarshal(0x%x) = %f, want %f", b, f64, tc.f64) + } + }) + } } -} */ func TestShortestFloatNone(t *testing.T) { testCases := []struct { @@ -2804,12 +2840,30 @@ func TestShortestFloatNone(t *testing.T) { } func TestInvalidShortestFloat(t *testing.T) { - wantErrorMsg := "cbor: invalid ShortestFloatMode 100" - _, err := EncOptions{ShortestFloat: ShortestFloatMode(100)}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{ShortestFloat: -1}, + wantErrorMsg: "cbor: invalid ShortestFloatMode -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{ShortestFloat: 101}, + wantErrorMsg: "cbor: invalid ShortestFloatMode 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -2848,12 +2902,30 @@ func TestInfConvert(t *testing.T) { } func TestInvalidInfConvert(t *testing.T) { - wantErrorMsg := "cbor: invalid InfConvertMode 100" - _, err := EncOptions{InfConvert: InfConvertMode(100)}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{InfConvert: -1}, + wantErrorMsg: "cbor: invalid InfConvertMode -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{InfConvert: 101}, + wantErrorMsg: "cbor: invalid InfConvertMode 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -2893,12 +2965,30 @@ func TestNilContainers(t *testing.T) { } func TestInvalidNilContainers(t *testing.T) { - wantErrorMsg := "cbor: invalid NilContainers 100" - _, err := EncOptions{NilContainers: NilContainersMode(100)}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{NilContainers: -1}, + wantErrorMsg: "cbor: invalid NilContainers -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{NilContainers: 101}, + wantErrorMsg: "cbor: invalid NilContainers 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -3150,12 +3240,30 @@ func TestNaNConvert(t *testing.T) { } func TestInvalidNaNConvert(t *testing.T) { - wantErrorMsg := "cbor: invalid NaNConvertMode 100" - _, err := EncOptions{NaNConvert: NaNConvertMode(100)}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{NaNConvert: -1}, + wantErrorMsg: "cbor: invalid NaNConvertMode -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{NaNConvert: 101}, + wantErrorMsg: "cbor: invalid NaNConvertMode 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -3317,32 +3425,86 @@ func TestPreferredUnsortedEncOptions(t *testing.T) { } func TestEncModeInvalidIndefiniteLengthMode(t *testing.T) { - wantErrorMsg := "cbor: invalid IndefLength 101" - _, err := EncOptions{IndefLength: 101}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{IndefLength: -1}, + wantErrorMsg: "cbor: invalid IndefLength -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{IndefLength: 101}, + wantErrorMsg: "cbor: invalid IndefLength 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } func TestEncModeInvalidTagsMode(t *testing.T) { - wantErrorMsg := "cbor: invalid TagsMd 101" - _, err := EncOptions{TagsMd: 101}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{TagsMd: -1}, + wantErrorMsg: "cbor: invalid TagsMd -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{TagsMd: 101}, + wantErrorMsg: "cbor: invalid TagsMd 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } func TestEncModeInvalidBigIntConvertMode(t *testing.T) { - wantErrorMsg := "cbor: invalid BigIntConvertMode 101" - _, err := EncOptions{BigIntConvert: 101}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{BigIntConvert: -1}, + wantErrorMsg: "cbor: invalid BigIntConvertMode -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{BigIntConvert: 101}, + wantErrorMsg: "cbor: invalid BigIntConvertMode 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } @@ -3371,12 +3533,30 @@ func TestEncOptions(t *testing.T) { } func TestEncModeInvalidTimeTag(t *testing.T) { - wantErrorMsg := "cbor: invalid TimeTag 100" - _, err := EncOptions{TimeTag: 100}.EncMode() - if err == nil { - t.Errorf("EncMode() didn't return an error") - } else if err.Error() != wantErrorMsg { - t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg) + for _, tc := range []struct { + name string + opts EncOptions + wantErrorMsg string + }{ + { + name: "below range of valid modes", + opts: EncOptions{TimeTag: -1}, + wantErrorMsg: "cbor: invalid TimeTag -1", + }, + { + name: "above range of valid modes", + opts: EncOptions{TimeTag: 101}, + wantErrorMsg: "cbor: invalid TimeTag 101", + }, + } { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.opts.EncMode() + if err == nil { + t.Errorf("EncMode() didn't return an error") + } else if err.Error() != tc.wantErrorMsg { + t.Errorf("EncMode() returned error %q, want %q", err.Error(), tc.wantErrorMsg) + } + }) } } diff --git a/tag.go b/tag.go index 5205aa66..aefb4d35 100644 --- a/tag.go +++ b/tag.go @@ -90,7 +90,7 @@ const ( ) func (dtm DecTagMode) valid() bool { - return dtm < maxDecTagMode + return dtm >= 0 && dtm < maxDecTagMode } // EncTagMode specifies how encoder handles tag number. @@ -107,7 +107,7 @@ const ( ) func (etm EncTagMode) valid() bool { - return etm < maxEncTagMode + return etm >= 0 && etm < maxEncTagMode } // TagOptions specifies how encoder and decoder handle tag number.