diff options
Diffstat (limited to 'vendor/github.com/fxamacker/cbor/v2/encode.go')
| -rw-r--r-- | vendor/github.com/fxamacker/cbor/v2/encode.go | 1989 |
1 files changed, 0 insertions, 1989 deletions
diff --git a/vendor/github.com/fxamacker/cbor/v2/encode.go b/vendor/github.com/fxamacker/cbor/v2/encode.go deleted file mode 100644 index 6508e291d..000000000 --- a/vendor/github.com/fxamacker/cbor/v2/encode.go +++ /dev/null @@ -1,1989 +0,0 @@ -// Copyright (c) Faye Amacker. All rights reserved. -// Licensed under the MIT License. See LICENSE in the project root for license information. - -package cbor - -import ( - "bytes" - "encoding" - "encoding/binary" - "errors" - "fmt" - "io" - "math" - "math/big" - "math/rand" - "reflect" - "sort" - "strconv" - "sync" - "time" - - "github.com/x448/float16" -) - -// Marshal returns the CBOR encoding of v using default encoding options. -// See EncOptions for encoding options. -// -// Marshal uses the following encoding rules: -// -// If value implements the Marshaler interface, Marshal calls its -// MarshalCBOR method. -// -// If value implements encoding.BinaryMarshaler, Marhsal calls its -// MarshalBinary method and encode it as CBOR byte string. -// -// Boolean values encode as CBOR booleans (type 7). -// -// Positive integer values encode as CBOR positive integers (type 0). -// -// Negative integer values encode as CBOR negative integers (type 1). -// -// Floating point values encode as CBOR floating points (type 7). -// -// String values encode as CBOR text strings (type 3). -// -// []byte values encode as CBOR byte strings (type 2). -// -// Array and slice values encode as CBOR arrays (type 4). -// -// Map values encode as CBOR maps (type 5). -// -// Struct values encode as CBOR maps (type 5). Each exported struct field -// becomes a pair with field name encoded as CBOR text string (type 3) and -// field value encoded based on its type. See struct tag option "keyasint" -// to encode field name as CBOR integer (type 0 and 1). Also see struct -// tag option "toarray" for special field "_" to encode struct values as -// CBOR array (type 4). -// -// Marshal supports format string stored under the "cbor" key in the struct -// field's tag. CBOR format string can specify the name of the field, -// "omitempty" and "keyasint" options, and special case "-" for field omission. -// If "cbor" key is absent, Marshal uses "json" key. -// -// Struct field name is treated as integer if it has "keyasint" option in -// its format string. The format string must specify an integer as its -// field name. -// -// Special struct field "_" is used to specify struct level options, such as -// "toarray". "toarray" option enables Go struct to be encoded as CBOR array. -// "omitempty" is disabled by "toarray" to ensure that the same number -// of elements are encoded every time. -// -// Anonymous struct fields are marshaled as if their exported fields -// were fields in the outer struct. Marshal follows the same struct fields -// visibility rules used by JSON encoding package. -// -// time.Time values encode as text strings specified in RFC3339 or numerical -// representation of seconds since January 1, 1970 UTC depending on -// EncOptions.Time setting. Also See EncOptions.TimeTag to encode -// time.Time as CBOR tag with tag number 0 or 1. -// -// big.Int values encode as CBOR integers (type 0 and 1) if values fit. -// Otherwise, big.Int values encode as CBOR bignums (tag 2 and 3). See -// EncOptions.BigIntConvert to always encode big.Int values as CBOR -// bignums. -// -// Pointer values encode as the value pointed to. -// -// Interface values encode as the value stored in the interface. -// -// Nil slice/map/pointer/interface values encode as CBOR nulls (type 7). -// -// Values of other types cannot be encoded in CBOR. Attempting -// to encode such a value causes Marshal to return an UnsupportedTypeError. -func Marshal(v interface{}) ([]byte, error) { - return defaultEncMode.Marshal(v) -} - -// MarshalToBuffer encodes v into provided buffer (instead of using built-in buffer pool) -// and uses default encoding options. -// -// NOTE: Unlike Marshal, the buffer provided to MarshalToBuffer can contain -// partially encoded data if error is returned. -// -// See Marshal for more details. -func MarshalToBuffer(v interface{}, buf *bytes.Buffer) error { - return defaultEncMode.MarshalToBuffer(v, buf) -} - -// Marshaler is the interface implemented by types that can marshal themselves -// into valid CBOR. -type Marshaler interface { - MarshalCBOR() ([]byte, error) -} - -// MarshalerError represents error from checking encoded CBOR data item -// returned from MarshalCBOR for well-formedness and some very limited tag validation. -type MarshalerError struct { - typ reflect.Type - err error -} - -func (e *MarshalerError) Error() string { - return "cbor: error calling MarshalCBOR for type " + - e.typ.String() + - ": " + e.err.Error() -} - -func (e *MarshalerError) Unwrap() error { - return e.err -} - -// UnsupportedTypeError is returned by Marshal when attempting to encode value -// of an unsupported type. -type UnsupportedTypeError struct { - Type reflect.Type -} - -func (e *UnsupportedTypeError) Error() string { - return "cbor: unsupported type: " + e.Type.String() -} - -// UnsupportedValueError is returned by Marshal when attempting to encode an -// unsupported value. -type UnsupportedValueError struct { - msg string -} - -func (e *UnsupportedValueError) Error() string { - return "cbor: unsupported value: " + e.msg -} - -// SortMode identifies supported sorting order. -type SortMode int - -const ( - // SortNone encodes map pairs and struct fields in an arbitrary order. - SortNone SortMode = 0 - - // SortLengthFirst causes map keys or struct fields to be sorted such that: - // - If two keys have different lengths, the shorter one sorts earlier; - // - If two keys have the same length, the one with the lower value in - // (byte-wise) lexical order sorts earlier. - // It is used in "Canonical CBOR" encoding in RFC 7049 3.9. - SortLengthFirst SortMode = 1 - - // SortBytewiseLexical causes map keys or struct fields to be sorted in the - // bytewise lexicographic order of their deterministic CBOR encodings. - // It is used in "CTAP2 Canonical CBOR" and "Core Deterministic Encoding" - // in RFC 7049bis. - SortBytewiseLexical SortMode = 2 - - // SortShuffle encodes map pairs and struct fields in a shuffled - // order. This mode does not guarantee an unbiased permutation, but it - // does guarantee that the runtime of the shuffle algorithm used will be - // constant. - SortFastShuffle SortMode = 3 - - // SortCanonical is used in "Canonical CBOR" encoding in RFC 7049 3.9. - SortCanonical SortMode = SortLengthFirst - - // SortCTAP2 is used in "CTAP2 Canonical CBOR". - SortCTAP2 SortMode = SortBytewiseLexical - - // SortCoreDeterministic is used in "Core Deterministic Encoding" in RFC 7049bis. - SortCoreDeterministic SortMode = SortBytewiseLexical - - maxSortMode SortMode = 4 -) - -func (sm SortMode) valid() bool { - return sm >= 0 && sm < maxSortMode -} - -// StringMode specifies how to encode Go string values. -type StringMode int - -const ( - // StringToTextString encodes Go string to CBOR text string (major type 3). - StringToTextString StringMode = iota - - // StringToByteString encodes Go string to CBOR byte string (major type 2). - StringToByteString -) - -func (st StringMode) cborType() (cborType, error) { - switch st { - case StringToTextString: - return cborTypeTextString, nil - - case StringToByteString: - return cborTypeByteString, nil - } - return 0, errors.New("cbor: invalid StringType " + strconv.Itoa(int(st))) -} - -// ShortestFloatMode specifies which floating-point format should -// be used as the shortest possible format for CBOR encoding. -// It is not used for encoding Infinity and NaN values. -type ShortestFloatMode int - -const ( - // ShortestFloatNone makes float values encode without any conversion. - // This is the default for ShortestFloatMode in v1. - // E.g. a float32 in Go will encode to CBOR float32. And - // a float64 in Go will encode to CBOR float64. - ShortestFloatNone ShortestFloatMode = iota - - // ShortestFloat16 specifies float16 as the shortest form that preserves value. - // E.g. if float64 can convert to float32 while preserving value, then - // encoding will also try to convert float32 to float16. So a float64 might - // encode as CBOR float64, float32 or float16 depending on the value. - ShortestFloat16 - - maxShortestFloat -) - -func (sfm ShortestFloatMode) valid() bool { - return sfm >= 0 && sfm < maxShortestFloat -} - -// NaNConvertMode specifies how to encode NaN and overrides ShortestFloatMode. -// ShortestFloatMode is not used for encoding Infinity and NaN values. -type NaNConvertMode int - -const ( - // NaNConvert7e00 always encodes NaN to 0xf97e00 (CBOR float16 = 0x7e00). - NaNConvert7e00 NaNConvertMode = iota - - // NaNConvertNone never modifies or converts NaN to other representations - // (float64 NaN stays float64, etc. even if it can use float16 without losing - // any bits). - NaNConvertNone - - // NaNConvertPreserveSignal converts NaN to the smallest form that preserves - // value (quiet bit + payload) as described in RFC 7049bis Draft 12. - NaNConvertPreserveSignal - - // NaNConvertQuiet always forces quiet bit = 1 and shortest form that preserves - // NaN payload. - NaNConvertQuiet - - // NaNConvertReject returns UnsupportedValueError on attempts to encode a NaN value. - NaNConvertReject - - maxNaNConvert -) - -func (ncm NaNConvertMode) valid() bool { - return ncm >= 0 && ncm < maxNaNConvert -} - -// InfConvertMode specifies how to encode Infinity and overrides ShortestFloatMode. -// ShortestFloatMode is not used for encoding Infinity and NaN values. -type InfConvertMode int - -const ( - // InfConvertFloat16 always converts Inf to lossless IEEE binary16 (float16). - InfConvertFloat16 InfConvertMode = iota - - // InfConvertNone never converts (used by CTAP2 Canonical CBOR). - InfConvertNone - - // InfConvertReject returns UnsupportedValueError on attempts to encode an infinite value. - InfConvertReject - - maxInfConvert -) - -func (icm InfConvertMode) valid() bool { - return icm >= 0 && icm < maxInfConvert -} - -// TimeMode specifies how to encode time.Time values. -type TimeMode int - -const ( - // TimeUnix causes time.Time to be encoded as epoch time in integer with second precision. - TimeUnix TimeMode = iota - - // TimeUnixMicro causes time.Time to be encoded as epoch time in float-point rounded to microsecond precision. - TimeUnixMicro - - // TimeUnixDynamic causes time.Time to be encoded as integer if time.Time doesn't have fractional seconds, - // otherwise float-point rounded to microsecond precision. - TimeUnixDynamic - - // TimeRFC3339 causes time.Time to be encoded as RFC3339 formatted string with second precision. - TimeRFC3339 - - // TimeRFC3339Nano causes time.Time to be encoded as RFC3339 formatted string with nanosecond precision. - TimeRFC3339Nano - - maxTimeMode -) - -func (tm TimeMode) valid() bool { - return tm >= 0 && tm < maxTimeMode -} - -// BigIntConvertMode specifies how to encode big.Int values. -type BigIntConvertMode int - -const ( - // BigIntConvertShortest makes big.Int encode to CBOR integer if value fits. - // E.g. if big.Int value can be converted to CBOR integer while preserving - // value, encoder will encode it to CBOR integer (major type 0 or 1). - BigIntConvertShortest BigIntConvertMode = iota - - // BigIntConvertNone makes big.Int encode to CBOR bignum (tag 2 or 3) without - // converting it to another CBOR type. - BigIntConvertNone - - // BigIntConvertReject returns an UnsupportedTypeError instead of marshaling a big.Int. - BigIntConvertReject - - maxBigIntConvert -) - -func (bim BigIntConvertMode) valid() bool { - return bim >= 0 && bim < maxBigIntConvert -} - -// NilContainersMode specifies how to encode nil slices and maps. -type NilContainersMode int - -const ( - // NilContainerAsNull encodes nil slices and maps as CBOR null. - // This is the default. - NilContainerAsNull NilContainersMode = iota - - // NilContainerAsEmpty encodes nil slices and maps as - // empty container (CBOR bytestring, array, or map). - NilContainerAsEmpty - - maxNilContainersMode -) - -func (m NilContainersMode) valid() bool { - return m >= 0 && m < maxNilContainersMode -} - -// OmitEmptyMode specifies how to encode struct fields with omitempty tag. -// The default behavior omits if field value would encode as empty CBOR value. -type OmitEmptyMode int - -const ( - // OmitEmptyCBORValue specifies that struct fields tagged with "omitempty" - // should be omitted from encoding if the field would be encoded as an empty - // CBOR value, such as CBOR false, 0, 0.0, nil, empty byte, empty string, - // empty array, or empty map. - OmitEmptyCBORValue OmitEmptyMode = iota - - // OmitEmptyGoValue specifies that struct fields tagged with "omitempty" - // should be omitted from encoding if the field has an empty Go value, - // defined as false, 0, 0.0, a nil pointer, a nil interface value, and - // any empty array, slice, map, or string. - // This behavior is the same as the current (aka v1) encoding/json package - // included in Go. - OmitEmptyGoValue - - maxOmitEmptyMode -) - -func (om OmitEmptyMode) valid() bool { - return om >= 0 && om < maxOmitEmptyMode -} - -// FieldNameMode specifies the CBOR type to use when encoding struct field names. -type FieldNameMode int - -const ( - // FieldNameToTextString encodes struct fields to CBOR text string (major type 3). - FieldNameToTextString FieldNameMode = iota - - // FieldNameToTextString encodes struct fields to CBOR byte string (major type 2). - FieldNameToByteString - - maxFieldNameMode -) - -func (fnm FieldNameMode) valid() bool { - return fnm >= 0 && fnm < maxFieldNameMode -} - -// ByteSliceLaterFormatMode specifies which later format conversion hint (CBOR tag 21-23) -// to include (if any) when encoding Go byte slice to CBOR byte string. The encoder will -// always encode unmodified bytes from the byte slice and just wrap it within -// CBOR tag 21, 22, or 23 if specified. -// See "Expected Later Encoding for CBOR-to-JSON Converters" in RFC 8949 Section 3.4.5.2. -type ByteSliceLaterFormatMode int - -const ( - // ByteSliceLaterFormatNone encodes unmodified bytes from Go byte slice to CBOR byte string (major type 2) - // without adding CBOR tag 21, 22, or 23. - ByteSliceLaterFormatNone ByteSliceLaterFormatMode = iota - - // ByteSliceLaterFormatBase64URL encodes unmodified bytes from Go byte slice to CBOR byte string (major type 2) - // inside CBOR tag 21 (expected later conversion to base64url encoding, see RFC 8949 Section 3.4.5.2). - ByteSliceLaterFormatBase64URL - - // ByteSliceLaterFormatBase64 encodes unmodified bytes from Go byte slice to CBOR byte string (major type 2) - // inside CBOR tag 22 (expected later conversion to base64 encoding, see RFC 8949 Section 3.4.5.2). - ByteSliceLaterFormatBase64 - - // ByteSliceLaterFormatBase16 encodes unmodified bytes from Go byte slice to CBOR byte string (major type 2) - // inside CBOR tag 23 (expected later conversion to base16 encoding, see RFC 8949 Section 3.4.5.2). - ByteSliceLaterFormatBase16 -) - -func (bsefm ByteSliceLaterFormatMode) encodingTag() (uint64, error) { - switch bsefm { - case ByteSliceLaterFormatNone: - return 0, nil - - case ByteSliceLaterFormatBase64URL: - return tagNumExpectedLaterEncodingBase64URL, nil - - case ByteSliceLaterFormatBase64: - return tagNumExpectedLaterEncodingBase64, nil - - case ByteSliceLaterFormatBase16: - return tagNumExpectedLaterEncodingBase16, nil - } - return 0, errors.New("cbor: invalid ByteSliceLaterFormat " + strconv.Itoa(int(bsefm))) -} - -// ByteArrayMode specifies how to encode byte arrays. -type ByteArrayMode int - -const ( - // ByteArrayToByteSlice encodes byte arrays the same way that a byte slice with identical - // length and contents is encoded. - ByteArrayToByteSlice ByteArrayMode = iota - - // ByteArrayToArray encodes byte arrays to the CBOR array type with one unsigned integer - // item for each byte in the array. - ByteArrayToArray - - maxByteArrayMode -) - -func (bam ByteArrayMode) valid() bool { - return bam >= 0 && bam < maxByteArrayMode -} - -// BinaryMarshalerMode specifies how to encode types that implement encoding.BinaryMarshaler. -type BinaryMarshalerMode int - -const ( - // BinaryMarshalerByteString encodes the output of MarshalBinary to a CBOR byte string. - BinaryMarshalerByteString BinaryMarshalerMode = iota - - // BinaryMarshalerNone does not recognize BinaryMarshaler implementations during encode. - BinaryMarshalerNone - - maxBinaryMarshalerMode -) - -func (bmm BinaryMarshalerMode) valid() bool { - return bmm >= 0 && bmm < maxBinaryMarshalerMode -} - -// EncOptions specifies encoding options. -type EncOptions struct { - // Sort specifies sorting order. - Sort SortMode - - // ShortestFloat specifies the shortest floating-point encoding that preserves - // the value being encoded. - ShortestFloat ShortestFloatMode - - // NaNConvert specifies how to encode NaN and it overrides ShortestFloatMode. - NaNConvert NaNConvertMode - - // InfConvert specifies how to encode Inf and it overrides ShortestFloatMode. - InfConvert InfConvertMode - - // BigIntConvert specifies how to encode big.Int values. - BigIntConvert BigIntConvertMode - - // Time specifies how to encode time.Time. - Time TimeMode - - // TimeTag allows time.Time to be encoded with a tag number. - // RFC3339 format gets tag number 0, and numeric epoch time tag number 1. - TimeTag EncTagMode - - // IndefLength specifies whether to allow indefinite length CBOR items. - IndefLength IndefLengthMode - - // NilContainers specifies how to encode nil slices and maps. - NilContainers NilContainersMode - - // TagsMd specifies whether to allow CBOR tags (major type 6). - TagsMd TagsMode - - // OmitEmptyMode specifies how to encode struct fields with omitempty tag. - OmitEmpty OmitEmptyMode - - // String specifies which CBOR type to use when encoding Go strings. - // - CBOR text string (major type 3) is default - // - CBOR byte string (major type 2) - String StringMode - - // FieldName specifies the CBOR type to use when encoding struct field names. - FieldName FieldNameMode - - // ByteSliceLaterFormat specifies which later format conversion hint (CBOR tag 21-23) - // to include (if any) when encoding Go byte slice to CBOR byte string. The encoder will - // always encode unmodified bytes from the byte slice and just wrap it within - // CBOR tag 21, 22, or 23 if specified. - // See "Expected Later Encoding for CBOR-to-JSON Converters" in RFC 8949 Section 3.4.5.2. - ByteSliceLaterFormat ByteSliceLaterFormatMode - - // ByteArray specifies how to encode byte arrays. - ByteArray ByteArrayMode - - // BinaryMarshaler specifies how to encode types that implement encoding.BinaryMarshaler. - BinaryMarshaler BinaryMarshalerMode -} - -// CanonicalEncOptions returns EncOptions for "Canonical CBOR" encoding, -// defined in RFC 7049 Section 3.9 with the following rules: -// -// 1. "Integers must be as small as possible." -// 2. "The expression of lengths in major types 2 through 5 must be as short as possible." -// 3. The keys in every map must be sorted in length-first sorting order. -// See SortLengthFirst for details. -// 4. "Indefinite-length items must be made into definite-length items." -// 5. "If a protocol allows for IEEE floats, then additional canonicalization rules might -// need to be added. One example rule might be to have all floats start as a 64-bit -// float, then do a test conversion to a 32-bit float; if the result is the same numeric -// value, use the shorter value and repeat the process with a test conversion to a -// 16-bit float. (This rule selects 16-bit float for positive and negative Infinity -// as well.) Also, there are many representations for NaN. If NaN is an allowed value, -// it must always be represented as 0xf97e00." -func CanonicalEncOptions() EncOptions { - return EncOptions{ - Sort: SortCanonical, - ShortestFloat: ShortestFloat16, - NaNConvert: NaNConvert7e00, - InfConvert: InfConvertFloat16, - IndefLength: IndefLengthForbidden, - } -} - -// CTAP2EncOptions returns EncOptions for "CTAP2 Canonical CBOR" encoding, -// defined in CTAP specification, with the following rules: -// -// 1. "Integers must be encoded as small as possible." -// 2. "The representations of any floating-point values are not changed." -// 3. "The expression of lengths in major types 2 through 5 must be as short as possible." -// 4. "Indefinite-length items must be made into definite-length items."" -// 5. The keys in every map must be sorted in bytewise lexicographic order. -// See SortBytewiseLexical for details. -// 6. "Tags as defined in Section 2.4 in [RFC7049] MUST NOT be present." -func CTAP2EncOptions() EncOptions { - return EncOptions{ - Sort: SortCTAP2, - ShortestFloat: ShortestFloatNone, - NaNConvert: NaNConvertNone, - InfConvert: InfConvertNone, - IndefLength: IndefLengthForbidden, - TagsMd: TagsForbidden, - } -} - -// CoreDetEncOptions returns EncOptions for "Core Deterministic" encoding, -// defined in RFC 7049bis with the following rules: -// -// 1. "Preferred serialization MUST be used. In particular, this means that arguments -// (see Section 3) for integers, lengths in major types 2 through 5, and tags MUST -// be as short as possible" -// "Floating point values also MUST use the shortest form that preserves the value" -// 2. "Indefinite-length items MUST NOT appear." -// 3. "The keys in every map MUST be sorted in the bytewise lexicographic order of -// their deterministic encodings." -func CoreDetEncOptions() EncOptions { - return EncOptions{ - Sort: SortCoreDeterministic, - ShortestFloat: ShortestFloat16, - NaNConvert: NaNConvert7e00, - InfConvert: InfConvertFloat16, - IndefLength: IndefLengthForbidden, - } -} - -// PreferredUnsortedEncOptions returns EncOptions for "Preferred Serialization" encoding, -// defined in RFC 7049bis with the following rules: -// -// 1. "The preferred serialization always uses the shortest form of representing the argument -// (Section 3);" -// 2. "it also uses the shortest floating-point encoding that preserves the value being -// encoded (see Section 5.5)." -// "The preferred encoding for a floating-point value is the shortest floating-point encoding -// that preserves its value, e.g., 0xf94580 for the number 5.5, and 0xfa45ad9c00 for the -// number 5555.5, unless the CBOR-based protocol specifically excludes the use of the shorter -// floating-point encodings. For NaN values, a shorter encoding is preferred if zero-padding -// the shorter significand towards the right reconstitutes the original NaN value (for many -// applications, the single NaN encoding 0xf97e00 will suffice)." -// 3. "Definite length encoding is preferred whenever the length is known at the time the -// serialization of the item starts." -func PreferredUnsortedEncOptions() EncOptions { - return EncOptions{ - Sort: SortNone, - ShortestFloat: ShortestFloat16, - NaNConvert: NaNConvert7e00, - InfConvert: InfConvertFloat16, - } -} - -// EncMode returns EncMode with immutable options and no tags (safe for concurrency). -func (opts EncOptions) EncMode() (EncMode, error) { //nolint:gocritic // ignore hugeParam - return opts.encMode() -} - -// UserBufferEncMode returns UserBufferEncMode with immutable options and no tags (safe for concurrency). -func (opts EncOptions) UserBufferEncMode() (UserBufferEncMode, 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) { //nolint:gocritic // ignore hugeParam - return opts.UserBufferEncModeWithTags(tags) -} - -// UserBufferEncModeWithTags returns UserBufferEncMode with options and tags that are both immutable (safe for concurrency). -func (opts EncOptions) UserBufferEncModeWithTags(tags TagSet) (UserBufferEncMode, error) { //nolint:gocritic // ignore hugeParam - if opts.TagsMd == TagsForbidden { - return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden") - } - if tags == nil { - return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet") - } - em, err := opts.encMode() - if err != nil { - return nil, err - } - // Copy tags - ts := tagSet(make(map[reflect.Type]*tagItem)) - syncTags := tags.(*syncTagSet) - syncTags.RLock() - for contentType, tag := range syncTags.t { - if tag.opts.EncTag != EncTagNone { - ts[contentType] = tag - } - } - syncTags.RUnlock() - if len(ts) > 0 { - em.tags = ts - } - return em, nil -} - -// EncModeWithSharedTags returns EncMode with immutable options and mutable shared tags (safe for concurrency). -func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) { //nolint:gocritic // ignore hugeParam - return opts.UserBufferEncModeWithSharedTags(tags) -} - -// UserBufferEncModeWithSharedTags returns UserBufferEncMode with immutable options and mutable shared tags (safe for concurrency). -func (opts EncOptions) UserBufferEncModeWithSharedTags(tags TagSet) (UserBufferEncMode, error) { //nolint:gocritic // ignore hugeParam - if opts.TagsMd == TagsForbidden { - return nil, errors.New("cbor: cannot create EncMode with TagSet when TagsMd is TagsForbidden") - } - if tags == nil { - return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet") - } - em, err := opts.encMode() - if err != nil { - return nil, err - } - em.tags = tags - return em, nil -} - -func (opts EncOptions) encMode() (*encMode, error) { //nolint:gocritic // ignore hugeParam - if !opts.Sort.valid() { - return nil, errors.New("cbor: invalid SortMode " + strconv.Itoa(int(opts.Sort))) - } - if !opts.ShortestFloat.valid() { - return nil, errors.New("cbor: invalid ShortestFloatMode " + strconv.Itoa(int(opts.ShortestFloat))) - } - if !opts.NaNConvert.valid() { - return nil, errors.New("cbor: invalid NaNConvertMode " + strconv.Itoa(int(opts.NaNConvert))) - } - if !opts.InfConvert.valid() { - return nil, errors.New("cbor: invalid InfConvertMode " + strconv.Itoa(int(opts.InfConvert))) - } - if !opts.BigIntConvert.valid() { - return nil, errors.New("cbor: invalid BigIntConvertMode " + strconv.Itoa(int(opts.BigIntConvert))) - } - if !opts.Time.valid() { - return nil, errors.New("cbor: invalid TimeMode " + strconv.Itoa(int(opts.Time))) - } - if !opts.TimeTag.valid() { - return nil, errors.New("cbor: invalid TimeTag " + strconv.Itoa(int(opts.TimeTag))) - } - if !opts.IndefLength.valid() { - return nil, errors.New("cbor: invalid IndefLength " + strconv.Itoa(int(opts.IndefLength))) - } - if !opts.NilContainers.valid() { - return nil, errors.New("cbor: invalid NilContainers " + strconv.Itoa(int(opts.NilContainers))) - } - if !opts.TagsMd.valid() { - return nil, errors.New("cbor: invalid TagsMd " + strconv.Itoa(int(opts.TagsMd))) - } - if opts.TagsMd == TagsForbidden && opts.TimeTag == EncTagRequired { - return nil, errors.New("cbor: cannot set TagsMd to TagsForbidden when TimeTag is EncTagRequired") - } - if !opts.OmitEmpty.valid() { - return nil, errors.New("cbor: invalid OmitEmpty " + strconv.Itoa(int(opts.OmitEmpty))) - } - stringMajorType, err := opts.String.cborType() - if err != nil { - return nil, err - } - if !opts.FieldName.valid() { - return nil, errors.New("cbor: invalid FieldName " + strconv.Itoa(int(opts.FieldName))) - } - byteSliceLaterEncodingTag, err := opts.ByteSliceLaterFormat.encodingTag() - if err != nil { - return nil, err - } - if !opts.ByteArray.valid() { - return nil, errors.New("cbor: invalid ByteArray " + strconv.Itoa(int(opts.ByteArray))) - } - if !opts.BinaryMarshaler.valid() { - return nil, errors.New("cbor: invalid BinaryMarshaler " + strconv.Itoa(int(opts.BinaryMarshaler))) - } - em := encMode{ - sort: opts.Sort, - shortestFloat: opts.ShortestFloat, - nanConvert: opts.NaNConvert, - infConvert: opts.InfConvert, - bigIntConvert: opts.BigIntConvert, - time: opts.Time, - timeTag: opts.TimeTag, - indefLength: opts.IndefLength, - nilContainers: opts.NilContainers, - tagsMd: opts.TagsMd, - omitEmpty: opts.OmitEmpty, - stringType: opts.String, - stringMajorType: stringMajorType, - fieldName: opts.FieldName, - byteSliceLaterFormat: opts.ByteSliceLaterFormat, - byteSliceLaterEncodingTag: byteSliceLaterEncodingTag, - byteArray: opts.ByteArray, - binaryMarshaler: opts.BinaryMarshaler, - } - return &em, nil -} - -// EncMode is the main interface for CBOR encoding. -type EncMode interface { - Marshal(v interface{}) ([]byte, error) - NewEncoder(w io.Writer) *Encoder - EncOptions() EncOptions -} - -// UserBufferEncMode is an interface for CBOR encoding, which extends EncMode by -// adding MarshalToBuffer to support user specified buffer rather than encoding -// into the built-in buffer pool. -type UserBufferEncMode interface { - EncMode - MarshalToBuffer(v interface{}, buf *bytes.Buffer) error - - // This private method is to prevent users implementing - // this interface and so future additions to it will - // not be breaking changes. - // See https://go.dev/blog/module-compatibility - unexport() -} - -type encMode struct { - tags tagProvider - sort SortMode - shortestFloat ShortestFloatMode - nanConvert NaNConvertMode - infConvert InfConvertMode - bigIntConvert BigIntConvertMode - time TimeMode - timeTag EncTagMode - indefLength IndefLengthMode - nilContainers NilContainersMode - tagsMd TagsMode - omitEmpty OmitEmptyMode - stringType StringMode - stringMajorType cborType - fieldName FieldNameMode - byteSliceLaterFormat ByteSliceLaterFormatMode - byteSliceLaterEncodingTag uint64 - byteArray ByteArrayMode - binaryMarshaler BinaryMarshalerMode -} - -var defaultEncMode, _ = EncOptions{}.encMode() - -// These four decoding modes are used by getMarshalerDecMode. -// maxNestedLevels, maxArrayElements, and maxMapPairs are -// set to max allowed limits to avoid rejecting Marshaler -// output that would have been the allowable output of a -// non-Marshaler object that exceeds default limits. -var ( - marshalerForbidIndefLengthForbidTagsDecMode = decMode{ - maxNestedLevels: maxMaxNestedLevels, - maxArrayElements: maxMaxArrayElements, - maxMapPairs: maxMaxMapPairs, - indefLength: IndefLengthForbidden, - tagsMd: TagsForbidden, - } - - marshalerAllowIndefLengthForbidTagsDecMode = decMode{ - maxNestedLevels: maxMaxNestedLevels, - maxArrayElements: maxMaxArrayElements, - maxMapPairs: maxMaxMapPairs, - indefLength: IndefLengthAllowed, - tagsMd: TagsForbidden, - } - - marshalerForbidIndefLengthAllowTagsDecMode = decMode{ - maxNestedLevels: maxMaxNestedLevels, - maxArrayElements: maxMaxArrayElements, - maxMapPairs: maxMaxMapPairs, - indefLength: IndefLengthForbidden, - tagsMd: TagsAllowed, - } - - marshalerAllowIndefLengthAllowTagsDecMode = decMode{ - maxNestedLevels: maxMaxNestedLevels, - maxArrayElements: maxMaxArrayElements, - maxMapPairs: maxMaxMapPairs, - indefLength: IndefLengthAllowed, - tagsMd: TagsAllowed, - } -) - -// getMarshalerDecMode returns one of four existing decoding modes -// which can be reused (safe for parallel use) for the purpose of -// checking if data returned by Marshaler is well-formed. -func getMarshalerDecMode(indefLength IndefLengthMode, tagsMd TagsMode) *decMode { - switch { - case indefLength == IndefLengthAllowed && tagsMd == TagsAllowed: - return &marshalerAllowIndefLengthAllowTagsDecMode - - case indefLength == IndefLengthAllowed && tagsMd == TagsForbidden: - return &marshalerAllowIndefLengthForbidTagsDecMode - - case indefLength == IndefLengthForbidden && tagsMd == TagsAllowed: - return &marshalerForbidIndefLengthAllowTagsDecMode - - case indefLength == IndefLengthForbidden && tagsMd == TagsForbidden: - return &marshalerForbidIndefLengthForbidTagsDecMode - - default: - // This should never happen, unless we add new options to - // IndefLengthMode or TagsMode without updating this function. - return &decMode{ - maxNestedLevels: maxMaxNestedLevels, - maxArrayElements: maxMaxArrayElements, - maxMapPairs: maxMaxMapPairs, - indefLength: indefLength, - tagsMd: tagsMd, - } - } -} - -// EncOptions returns user specified options used to create this EncMode. -func (em *encMode) EncOptions() EncOptions { - return EncOptions{ - Sort: em.sort, - ShortestFloat: em.shortestFloat, - NaNConvert: em.nanConvert, - InfConvert: em.infConvert, - BigIntConvert: em.bigIntConvert, - Time: em.time, - TimeTag: em.timeTag, - IndefLength: em.indefLength, - NilContainers: em.nilContainers, - TagsMd: em.tagsMd, - OmitEmpty: em.omitEmpty, - String: em.stringType, - FieldName: em.fieldName, - ByteSliceLaterFormat: em.byteSliceLaterFormat, - ByteArray: em.byteArray, - BinaryMarshaler: em.binaryMarshaler, - } -} - -func (em *encMode) unexport() {} - -func (em *encMode) encTagBytes(t reflect.Type) []byte { - if em.tags != nil { - if tagItem := em.tags.getTagItemFromType(t); tagItem != nil { - return tagItem.cborTagNum - } - } - return nil -} - -// Marshal returns the CBOR encoding of v using em encoding mode. -// -// See the documentation for Marshal for details. -func (em *encMode) Marshal(v interface{}) ([]byte, error) { - e := getEncodeBuffer() - - if err := encode(e, em, reflect.ValueOf(v)); err != nil { - putEncodeBuffer(e) - return nil, err - } - - buf := make([]byte, e.Len()) - copy(buf, e.Bytes()) - - putEncodeBuffer(e) - return buf, nil -} - -// MarshalToBuffer encodes v into provided buffer (instead of using built-in buffer pool) -// and uses em encoding mode. -// -// NOTE: Unlike Marshal, the buffer provided to MarshalToBuffer can contain -// partially encoded data if error is returned. -// -// See Marshal for more details. -func (em *encMode) MarshalToBuffer(v interface{}, buf *bytes.Buffer) error { - if buf == nil { - return fmt.Errorf("cbor: encoding buffer provided by user is nil") - } - return encode(buf, em, reflect.ValueOf(v)) -} - -// NewEncoder returns a new encoder that writes to w using em EncMode. -func (em *encMode) NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: w, em: em} -} - -// encodeBufferPool caches unused bytes.Buffer objects for later reuse. -var encodeBufferPool = sync.Pool{ - New: func() interface{} { - e := new(bytes.Buffer) - e.Grow(32) // TODO: make this configurable - return e - }, -} - -func getEncodeBuffer() *bytes.Buffer { - return encodeBufferPool.Get().(*bytes.Buffer) -} - -func putEncodeBuffer(e *bytes.Buffer) { - e.Reset() - encodeBufferPool.Put(e) -} - -type encodeFunc func(e *bytes.Buffer, em *encMode, v reflect.Value) error -type isEmptyFunc func(em *encMode, v reflect.Value) (empty bool, err error) - -func encode(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if !v.IsValid() { - // v is zero value - e.Write(cborNil) - return nil - } - vt := v.Type() - f, _ := getEncodeFunc(vt) - if f == nil { - return &UnsupportedTypeError{vt} - } - - return f(e, em, v) -} - -func encodeBool(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - b := cborFalse - if v.Bool() { - b = cborTrue - } - e.Write(b) - return nil -} - -func encodeInt(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - i := v.Int() - if i >= 0 { - encodeHead(e, byte(cborTypePositiveInt), uint64(i)) - return nil - } - i = i*(-1) - 1 - encodeHead(e, byte(cborTypeNegativeInt), uint64(i)) - return nil -} - -func encodeUint(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - encodeHead(e, byte(cborTypePositiveInt), v.Uint()) - return nil -} - -func encodeFloat(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - f64 := v.Float() - if math.IsNaN(f64) { - return encodeNaN(e, em, v) - } - if math.IsInf(f64, 0) { - return encodeInf(e, em, v) - } - fopt := em.shortestFloat - if v.Kind() == reflect.Float64 && (fopt == ShortestFloatNone || cannotFitFloat32(f64)) { - // Encode float64 - // Don't use encodeFloat64() because it cannot be inlined. - const argumentSize = 8 - const headSize = 1 + argumentSize - var scratch [headSize]byte - scratch[0] = byte(cborTypePrimitives) | byte(additionalInformationAsFloat64) - binary.BigEndian.PutUint64(scratch[1:], math.Float64bits(f64)) - e.Write(scratch[:]) - return nil - } - - f32 := float32(f64) - if fopt == ShortestFloat16 { - var f16 float16.Float16 - p := float16.PrecisionFromfloat32(f32) - if p == float16.PrecisionExact { - // Roundtrip float32->float16->float32 test isn't needed. - f16 = float16.Fromfloat32(f32) - } else if p == float16.PrecisionUnknown { - // Try roundtrip float32->float16->float32 to determine if float32 can fit into float16. - f16 = float16.Fromfloat32(f32) - if f16.Float32() == f32 { - p = float16.PrecisionExact - } - } - if p == float16.PrecisionExact { - // Encode float16 - // Don't use encodeFloat16() because it cannot be inlined. - const argumentSize = 2 - const headSize = 1 + argumentSize - var scratch [headSize]byte - scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat16 - binary.BigEndian.PutUint16(scratch[1:], uint16(f16)) - e.Write(scratch[:]) - return nil - } - } - - // Encode float32 - // Don't use encodeFloat32() because it cannot be inlined. - const argumentSize = 4 - const headSize = 1 + argumentSize - var scratch [headSize]byte - scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat32 - binary.BigEndian.PutUint32(scratch[1:], math.Float32bits(f32)) - e.Write(scratch[:]) - return nil -} - -func encodeInf(e *bytes.Buffer, em *encMode, v reflect.Value) error { - f64 := v.Float() - switch em.infConvert { - case InfConvertReject: - return &UnsupportedValueError{msg: "floating-point infinity"} - - case InfConvertFloat16: - if f64 > 0 { - e.Write(cborPositiveInfinity) - } else { - e.Write(cborNegativeInfinity) - } - return nil - } - if v.Kind() == reflect.Float64 { - return encodeFloat64(e, f64) - } - return encodeFloat32(e, float32(f64)) -} - -func encodeNaN(e *bytes.Buffer, em *encMode, v reflect.Value) error { - switch em.nanConvert { - case NaNConvert7e00: - e.Write(cborNaN) - return nil - - case NaNConvertNone: - if v.Kind() == reflect.Float64 { - return encodeFloat64(e, v.Float()) - } - f32 := float32NaNFromReflectValue(v) - return encodeFloat32(e, f32) - - case NaNConvertReject: - return &UnsupportedValueError{msg: "floating-point NaN"} - - default: // NaNConvertPreserveSignal, NaNConvertQuiet - if v.Kind() == reflect.Float64 { - f64 := v.Float() - f64bits := math.Float64bits(f64) - if em.nanConvert == NaNConvertQuiet && f64bits&(1<<51) == 0 { - f64bits |= 1 << 51 // Set quiet bit = 1 - f64 = math.Float64frombits(f64bits) - } - // The lower 29 bits are dropped when converting from float64 to float32. - if f64bits&0x1fffffff != 0 { - // Encode NaN as float64 because dropped coef bits from float64 to float32 are not all 0s. - return encodeFloat64(e, f64) - } - // Create float32 from float64 manually because float32(f64) always turns on NaN's quiet bits. - sign := uint32(f64bits>>32) & (1 << 31) - exp := uint32(0x7f800000) - coef := uint32((f64bits & 0xfffffffffffff) >> 29) - f32bits := sign | exp | coef - f32 := math.Float32frombits(f32bits) - // The lower 13 bits are dropped when converting from float32 to float16. - if f32bits&0x1fff != 0 { - // Encode NaN as float32 because dropped coef bits from float32 to float16 are not all 0s. - return encodeFloat32(e, f32) - } - // Encode NaN as float16 - f16, _ := float16.FromNaN32ps(f32) // Ignore err because it only returns error when f32 is not a NaN. - return encodeFloat16(e, f16) - } - - f32 := float32NaNFromReflectValue(v) - f32bits := math.Float32bits(f32) - if em.nanConvert == NaNConvertQuiet && f32bits&(1<<22) == 0 { - f32bits |= 1 << 22 // Set quiet bit = 1 - f32 = math.Float32frombits(f32bits) - } - // The lower 13 bits are dropped coef bits when converting from float32 to float16. - if f32bits&0x1fff != 0 { - // Encode NaN as float32 because dropped coef bits from float32 to float16 are not all 0s. - return encodeFloat32(e, f32) - } - f16, _ := float16.FromNaN32ps(f32) // Ignore err because it only returns error when f32 is not a NaN. - return encodeFloat16(e, f16) - } -} - -func encodeFloat16(e *bytes.Buffer, f16 float16.Float16) error { - const argumentSize = 2 - const headSize = 1 + argumentSize - var scratch [headSize]byte - scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat16 - binary.BigEndian.PutUint16(scratch[1:], uint16(f16)) - e.Write(scratch[:]) - return nil -} - -func encodeFloat32(e *bytes.Buffer, f32 float32) error { - const argumentSize = 4 - const headSize = 1 + argumentSize - var scratch [headSize]byte - scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat32 - binary.BigEndian.PutUint32(scratch[1:], math.Float32bits(f32)) - e.Write(scratch[:]) - return nil -} - -func encodeFloat64(e *bytes.Buffer, f64 float64) error { - const argumentSize = 8 - const headSize = 1 + argumentSize - var scratch [headSize]byte - scratch[0] = byte(cborTypePrimitives) | additionalInformationAsFloat64 - binary.BigEndian.PutUint64(scratch[1:], math.Float64bits(f64)) - e.Write(scratch[:]) - return nil -} - -func encodeByteString(e *bytes.Buffer, em *encMode, v reflect.Value) error { - vk := v.Kind() - if vk == reflect.Slice && v.IsNil() && em.nilContainers == NilContainerAsNull { - e.Write(cborNil) - return nil - } - if vk == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 && em.byteSliceLaterEncodingTag != 0 { - encodeHead(e, byte(cborTypeTag), em.byteSliceLaterEncodingTag) - } - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - slen := v.Len() - if slen == 0 { - return e.WriteByte(byte(cborTypeByteString)) - } - encodeHead(e, byte(cborTypeByteString), uint64(slen)) - if vk == reflect.Array { - for i := 0; i < slen; i++ { - e.WriteByte(byte(v.Index(i).Uint())) - } - return nil - } - e.Write(v.Bytes()) - return nil -} - -func encodeString(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - s := v.String() - encodeHead(e, byte(em.stringMajorType), uint64(len(s))) - e.WriteString(s) - return nil -} - -type arrayEncodeFunc struct { - f encodeFunc -} - -func (ae arrayEncodeFunc) encode(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if em.byteArray == ByteArrayToByteSlice && v.Type().Elem().Kind() == reflect.Uint8 { - return encodeByteString(e, em, v) - } - if v.Kind() == reflect.Slice && v.IsNil() && em.nilContainers == NilContainerAsNull { - e.Write(cborNil) - return nil - } - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - alen := v.Len() - if alen == 0 { - return e.WriteByte(byte(cborTypeArray)) - } - encodeHead(e, byte(cborTypeArray), uint64(alen)) - for i := 0; i < alen; i++ { - if err := ae.f(e, em, v.Index(i)); err != nil { - return err - } - } - return nil -} - -// encodeKeyValueFunc encodes key/value pairs in map (v). -// If kvs is provided (having the same length as v), length of encoded key and value are stored in kvs. -// kvs is used for canonical encoding of map. -type encodeKeyValueFunc func(e *bytes.Buffer, em *encMode, v reflect.Value, kvs []keyValue) error - -type mapEncodeFunc struct { - e encodeKeyValueFunc -} - -func (me mapEncodeFunc) encode(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if v.IsNil() && em.nilContainers == NilContainerAsNull { - e.Write(cborNil) - return nil - } - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - mlen := v.Len() - if mlen == 0 { - return e.WriteByte(byte(cborTypeMap)) - } - - encodeHead(e, byte(cborTypeMap), uint64(mlen)) - if em.sort == SortNone || em.sort == SortFastShuffle || mlen <= 1 { - return me.e(e, em, v, nil) - } - - kvsp := getKeyValues(v.Len()) // for sorting keys - defer putKeyValues(kvsp) - kvs := *kvsp - - kvBeginOffset := e.Len() - if err := me.e(e, em, v, kvs); err != nil { - return err - } - kvTotalLen := e.Len() - kvBeginOffset - - // Use the capacity at the tail of the encode buffer as a staging area to rearrange the - // encoded pairs into sorted order. - e.Grow(kvTotalLen) - tmp := e.Bytes()[e.Len() : e.Len()+kvTotalLen] // Can use e.AvailableBuffer() in Go 1.21+. - dst := e.Bytes()[kvBeginOffset:] - - if em.sort == SortBytewiseLexical { - sort.Sort(&bytewiseKeyValueSorter{kvs: kvs, data: dst}) - } else { - sort.Sort(&lengthFirstKeyValueSorter{kvs: kvs, data: dst}) - } - - // This is where the encoded bytes are actually rearranged in the output buffer to reflect - // the desired order. - sortedOffset := 0 - for _, kv := range kvs { - copy(tmp[sortedOffset:], dst[kv.offset:kv.nextOffset]) - sortedOffset += kv.nextOffset - kv.offset - } - copy(dst, tmp[:kvTotalLen]) - - return nil - -} - -// keyValue is the position of an encoded pair in a buffer. All offsets are zero-based and relative -// to the first byte of the first encoded pair. -type keyValue struct { - offset int - valueOffset int - nextOffset int -} - -type bytewiseKeyValueSorter struct { - kvs []keyValue - data []byte -} - -func (x *bytewiseKeyValueSorter) Len() int { - return len(x.kvs) -} - -func (x *bytewiseKeyValueSorter) Swap(i, j int) { - x.kvs[i], x.kvs[j] = x.kvs[j], x.kvs[i] -} - -func (x *bytewiseKeyValueSorter) Less(i, j int) bool { - kvi, kvj := x.kvs[i], x.kvs[j] - return bytes.Compare(x.data[kvi.offset:kvi.valueOffset], x.data[kvj.offset:kvj.valueOffset]) <= 0 -} - -type lengthFirstKeyValueSorter struct { - kvs []keyValue - data []byte -} - -func (x *lengthFirstKeyValueSorter) Len() int { - return len(x.kvs) -} - -func (x *lengthFirstKeyValueSorter) Swap(i, j int) { - x.kvs[i], x.kvs[j] = x.kvs[j], x.kvs[i] -} - -func (x *lengthFirstKeyValueSorter) Less(i, j int) bool { - kvi, kvj := x.kvs[i], x.kvs[j] - if keyLengthDifference := (kvi.valueOffset - kvi.offset) - (kvj.valueOffset - kvj.offset); keyLengthDifference != 0 { - return keyLengthDifference < 0 - } - return bytes.Compare(x.data[kvi.offset:kvi.valueOffset], x.data[kvj.offset:kvj.valueOffset]) <= 0 -} - -var keyValuePool = sync.Pool{} - -func getKeyValues(length int) *[]keyValue { - v := keyValuePool.Get() - if v == nil { - y := make([]keyValue, length) - return &y - } - x := v.(*[]keyValue) - if cap(*x) >= length { - *x = (*x)[:length] - return x - } - // []keyValue from the pool does not have enough capacity. - // Return it back to the pool and create a new one. - keyValuePool.Put(x) - y := make([]keyValue, length) - return &y -} - -func putKeyValues(x *[]keyValue) { - *x = (*x)[:0] - keyValuePool.Put(x) -} - -func encodeStructToArray(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) { - structType, err := getEncodingStructType(v.Type()) - if err != nil { - return err - } - - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - - flds := structType.fields - - encodeHead(e, byte(cborTypeArray), uint64(len(flds))) - for i := 0; i < len(flds); i++ { - f := flds[i] - - var fv reflect.Value - if len(f.idx) == 1 { - fv = v.Field(f.idx[0]) - } else { - // Get embedded field value. No error is expected. - fv, _ = getFieldValue(v, f.idx, func(reflect.Value) (reflect.Value, error) { - // Write CBOR nil for null pointer to embedded struct - e.Write(cborNil) - return reflect.Value{}, nil - }) - if !fv.IsValid() { - continue - } - } - - if err := f.ef(e, em, fv); err != nil { - return err - } - } - return nil -} - -func encodeStruct(e *bytes.Buffer, em *encMode, v reflect.Value) (err error) { - structType, err := getEncodingStructType(v.Type()) - if err != nil { - return err - } - - flds := structType.getFields(em) - - start := 0 - if em.sort == SortFastShuffle && len(flds) > 0 { - start = rand.Intn(len(flds)) //nolint:gosec // Don't need a CSPRNG for deck cutting. - } - - if b := em.encTagBytes(v.Type()); b != nil { - e.Write(b) - } - - // Encode head with struct field count. - // Head is rewritten later if actual encoded field count is different from struct field count. - encodedHeadLen := encodeHead(e, byte(cborTypeMap), uint64(len(flds))) - - kvbegin := e.Len() - kvcount := 0 - for offset := 0; offset < len(flds); offset++ { - f := flds[(start+offset)%len(flds)] - - var fv reflect.Value - if len(f.idx) == 1 { - fv = v.Field(f.idx[0]) - } else { - // Get embedded field value. No error is expected. - fv, _ = getFieldValue(v, f.idx, func(reflect.Value) (reflect.Value, error) { - // Skip null pointer to embedded struct - return reflect.Value{}, nil - }) - if !fv.IsValid() { - continue - } - } - if f.omitEmpty { - empty, err := f.ief(em, fv) - if err != nil { - return err - } - if empty { - continue - } - } - - if !f.keyAsInt && em.fieldName == FieldNameToByteString { - e.Write(f.cborNameByteString) - } else { // int or text string - e.Write(f.cborName) - } - - if err := f.ef(e, em, fv); err != nil { - return err - } - - kvcount++ - } - - if len(flds) == kvcount { - // Encoded element count in head is the same as actual element count. - return nil - } - - // Overwrite the bytes that were reserved for the head before encoding the map entries. - var actualHeadLen int - { - headbuf := *bytes.NewBuffer(e.Bytes()[kvbegin-encodedHeadLen : kvbegin-encodedHeadLen : kvbegin]) - actualHeadLen = encodeHead(&headbuf, byte(cborTypeMap), uint64(kvcount)) - } - - if actualHeadLen == encodedHeadLen { - // The bytes reserved for the encoded head were exactly the right size, so the - // encoded entries are already in their final positions. - return nil - } - - // We reserved more bytes than needed for the encoded head, based on the number of fields - // encoded. The encoded entries are offset to the right by the number of excess reserved - // bytes. Shift the entries left to remove the gap. - excessReservedBytes := encodedHeadLen - actualHeadLen - dst := e.Bytes()[kvbegin-excessReservedBytes : e.Len()-excessReservedBytes] - src := e.Bytes()[kvbegin:e.Len()] - copy(dst, src) - - // After shifting, the excess bytes are at the end of the output buffer and they are - // garbage. - e.Truncate(e.Len() - excessReservedBytes) - return nil -} - -func encodeIntf(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if v.IsNil() { - e.Write(cborNil) - return nil - } - return encode(e, em, v.Elem()) -} - -func encodeTime(e *bytes.Buffer, em *encMode, v reflect.Value) error { - t := v.Interface().(time.Time) - if t.IsZero() { - e.Write(cborNil) // Even if tag is required, encode as CBOR null. - return nil - } - if em.timeTag == EncTagRequired { - tagNumber := 1 - if em.time == TimeRFC3339 || em.time == TimeRFC3339Nano { - tagNumber = 0 - } - encodeHead(e, byte(cborTypeTag), uint64(tagNumber)) - } - switch em.time { - case TimeUnix: - secs := t.Unix() - return encodeInt(e, em, reflect.ValueOf(secs)) - - case TimeUnixMicro: - t = t.UTC().Round(time.Microsecond) - f := float64(t.UnixNano()) / 1e9 - return encodeFloat(e, em, reflect.ValueOf(f)) - - case TimeUnixDynamic: - t = t.UTC().Round(time.Microsecond) - secs, nsecs := t.Unix(), uint64(t.Nanosecond()) - if nsecs == 0 { - return encodeInt(e, em, reflect.ValueOf(secs)) - } - f := float64(secs) + float64(nsecs)/1e9 - return encodeFloat(e, em, reflect.ValueOf(f)) - - case TimeRFC3339: - s := t.Format(time.RFC3339) - return encodeString(e, em, reflect.ValueOf(s)) - - default: // TimeRFC3339Nano - s := t.Format(time.RFC3339Nano) - return encodeString(e, em, reflect.ValueOf(s)) - } -} - -func encodeBigInt(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if em.bigIntConvert == BigIntConvertReject { - return &UnsupportedTypeError{Type: typeBigInt} - } - - vbi := v.Interface().(big.Int) - sign := vbi.Sign() - bi := new(big.Int).SetBytes(vbi.Bytes()) // bi is absolute value of v - if sign < 0 { - // For negative number, convert to CBOR encoded number (-v-1). - bi.Sub(bi, big.NewInt(1)) - } - - if em.bigIntConvert == BigIntConvertShortest { - if bi.IsUint64() { - if sign >= 0 { - // Encode as CBOR pos int (major type 0) - encodeHead(e, byte(cborTypePositiveInt), bi.Uint64()) - return nil - } - // Encode as CBOR neg int (major type 1) - encodeHead(e, byte(cborTypeNegativeInt), bi.Uint64()) - return nil - } - } - - tagNum := 2 - if sign < 0 { - tagNum = 3 - } - // Write tag number - encodeHead(e, byte(cborTypeTag), uint64(tagNum)) - // Write bignum byte string - b := bi.Bytes() - encodeHead(e, byte(cborTypeByteString), uint64(len(b))) - e.Write(b) - return nil -} - -type binaryMarshalerEncoder struct { - alternateEncode encodeFunc - alternateIsEmpty isEmptyFunc -} - -func (bme binaryMarshalerEncoder) encode(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if em.binaryMarshaler != BinaryMarshalerByteString { - return bme.alternateEncode(e, em, v) - } - - vt := v.Type() - m, ok := v.Interface().(encoding.BinaryMarshaler) - if !ok { - pv := reflect.New(vt) - pv.Elem().Set(v) - m = pv.Interface().(encoding.BinaryMarshaler) - } - data, err := m.MarshalBinary() - if err != nil { - return err - } - if b := em.encTagBytes(vt); b != nil { - e.Write(b) - } - encodeHead(e, byte(cborTypeByteString), uint64(len(data))) - e.Write(data) - return nil -} - -func (bme binaryMarshalerEncoder) isEmpty(em *encMode, v reflect.Value) (bool, error) { - if em.binaryMarshaler != BinaryMarshalerByteString { - return bme.alternateIsEmpty(em, v) - } - - m, ok := v.Interface().(encoding.BinaryMarshaler) - if !ok { - pv := reflect.New(v.Type()) - pv.Elem().Set(v) - m = pv.Interface().(encoding.BinaryMarshaler) - } - data, err := m.MarshalBinary() - if err != nil { - return false, err - } - return len(data) == 0, nil -} - -func encodeMarshalerType(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if em.tagsMd == TagsForbidden && v.Type() == typeRawTag { - return errors.New("cbor: cannot encode cbor.RawTag when TagsMd is TagsForbidden") - } - m, ok := v.Interface().(Marshaler) - if !ok { - pv := reflect.New(v.Type()) - pv.Elem().Set(v) - m = pv.Interface().(Marshaler) - } - data, err := m.MarshalCBOR() - if err != nil { - return err - } - - // Verify returned CBOR data item from MarshalCBOR() is well-formed and passes tag validity for builtin tags 0-3. - d := decoder{data: data, dm: getMarshalerDecMode(em.indefLength, em.tagsMd)} - err = d.wellformed(false, true) - if err != nil { - return &MarshalerError{typ: v.Type(), err: err} - } - - e.Write(data) - return nil -} - -func encodeTag(e *bytes.Buffer, em *encMode, v reflect.Value) error { - if em.tagsMd == TagsForbidden { - return errors.New("cbor: cannot encode cbor.Tag when TagsMd is TagsForbidden") - } - - t := v.Interface().(Tag) - - if t.Number == 0 && t.Content == nil { - // Marshal uninitialized cbor.Tag - e.Write(cborNil) - return nil - } - - // Marshal tag number - encodeHead(e, byte(cborTypeTag), t.Number) - - vem := *em // shallow copy - - // For built-in tags, disable settings that may introduce tag validity errors when - // marshaling certain Content values. - switch t.Number { - case tagNumRFC3339Time: - vem.stringType = StringToTextString - vem.stringMajorType = cborTypeTextString - case tagNumUnsignedBignum, tagNumNegativeBignum: - vem.byteSliceLaterFormat = ByteSliceLaterFormatNone - vem.byteSliceLaterEncodingTag = 0 - } - - // Marshal tag content - return encode(e, &vem, reflect.ValueOf(t.Content)) -} - -// 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 <= maxAdditionalInformationWithoutArgument { - const headSize = 1 - e.WriteByte(t | byte(n)) - return headSize - } - - if n <= math.MaxUint8 { - const headSize = 2 - scratch := [headSize]byte{ - t | byte(additionalInformationWith1ByteArgument), - byte(n), - } - e.Write(scratch[:]) - return headSize - } - - if n <= math.MaxUint16 { - const headSize = 3 - var scratch [headSize]byte - scratch[0] = t | byte(additionalInformationWith2ByteArgument) - binary.BigEndian.PutUint16(scratch[1:], uint16(n)) - e.Write(scratch[:]) - return headSize - } - - if n <= math.MaxUint32 { - const headSize = 5 - var scratch [headSize]byte - scratch[0] = t | byte(additionalInformationWith4ByteArgument) - binary.BigEndian.PutUint32(scratch[1:], uint32(n)) - e.Write(scratch[:]) - return headSize - } - - const headSize = 9 - var scratch [headSize]byte - scratch[0] = t | byte(additionalInformationWith8ByteArgument) - binary.BigEndian.PutUint64(scratch[1:], n) - e.Write(scratch[:]) - return headSize -} - -var ( - typeMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem() - typeBinaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem() - typeRawMessage = reflect.TypeOf(RawMessage(nil)) - typeByteString = reflect.TypeOf(ByteString("")) -) - -func getEncodeFuncInternal(t reflect.Type) (ef encodeFunc, ief isEmptyFunc) { - k := t.Kind() - if k == reflect.Ptr { - return getEncodeIndirectValueFunc(t), isEmptyPtr - } - 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 - } - if reflect.PtrTo(t).Implements(typeMarshaler) { - return encodeMarshalerType, alwaysNotEmpty - } - if reflect.PtrTo(t).Implements(typeBinaryMarshaler) { - defer func() { - // capture encoding method used for modes that disable BinaryMarshaler - bme := binaryMarshalerEncoder{ - alternateEncode: ef, - alternateIsEmpty: ief, - } - ef = bme.encode - ief = bme.isEmpty - }() - } - 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 { - tag := f.Tag.Get("cbor") - if tag != "-" { - if hasToArrayOption(tag) { - return encodeStructToArray, isEmptyStruct - } - } - } - return encodeStruct, isEmptyStruct - - case reflect.Interface: - return encodeIntf, isEmptyIntf - } - return nil, nil -} - -func getEncodeIndirectValueFunc(t reflect.Type) encodeFunc { - for t.Kind() == reflect.Ptr { - t = t.Elem() - } - f, _ := getEncodeFunc(t) - if f == nil { - return nil - } - return func(e *bytes.Buffer, em *encMode, v reflect.Value) error { - for v.Kind() == reflect.Ptr && !v.IsNil() { - v = v.Elem() - } - if v.Kind() == reflect.Ptr && v.IsNil() { - e.Write(cborNil) - return nil - } - return f(e, em, v) - } -} - -func alwaysNotEmpty(_ *encMode, _ reflect.Value) (empty bool, err error) { - return false, nil -} - -func isEmptyBool(_ *encMode, v reflect.Value) (bool, error) { - return !v.Bool(), nil -} - -func isEmptyInt(_ *encMode, v reflect.Value) (bool, error) { - return v.Int() == 0, nil -} - -func isEmptyUint(_ *encMode, v reflect.Value) (bool, error) { - return v.Uint() == 0, nil -} - -func isEmptyFloat(_ *encMode, v reflect.Value) (bool, error) { - return v.Float() == 0.0, nil -} - -func isEmptyString(_ *encMode, v reflect.Value) (bool, error) { - return v.Len() == 0, nil -} - -func isEmptySlice(_ *encMode, v reflect.Value) (bool, error) { - return v.Len() == 0, nil -} - -func isEmptyMap(_ *encMode, v reflect.Value) (bool, error) { - return v.Len() == 0, nil -} - -func isEmptyPtr(_ *encMode, v reflect.Value) (bool, error) { - return v.IsNil(), nil -} - -func isEmptyIntf(_ *encMode, v reflect.Value) (bool, error) { - return v.IsNil(), nil -} - -func isEmptyStruct(em *encMode, v reflect.Value) (bool, error) { - structType, err := getEncodingStructType(v.Type()) - if err != nil { - return false, err - } - - if em.omitEmpty == OmitEmptyGoValue { - return false, nil - } - - if structType.toArray { - return len(structType.fields) == 0, nil - } - - if len(structType.fields) > len(structType.omitEmptyFieldsIdx) { - return false, nil - } - - for _, i := range structType.omitEmptyFieldsIdx { - f := structType.fields[i] - - // Get field value - var fv reflect.Value - if len(f.idx) == 1 { - fv = v.Field(f.idx[0]) - } else { - // Get embedded field value. No error is expected. - fv, _ = getFieldValue(v, f.idx, func(reflect.Value) (reflect.Value, error) { - // Skip null pointer to embedded struct - return reflect.Value{}, nil - }) - if !fv.IsValid() { - continue - } - } - - empty, err := f.ief(em, fv) - if err != nil { - return false, err - } - if !empty { - return false, nil - } - } - return true, nil -} - -func cannotFitFloat32(f64 float64) bool { - f32 := float32(f64) - return float64(f32) != f64 -} - -// float32NaNFromReflectValue extracts float32 NaN from reflect.Value while preserving NaN's quiet bit. -func float32NaNFromReflectValue(v reflect.Value) float32 { - // Keith Randall's workaround for issue https://github.com/golang/go/issues/36400 - p := reflect.New(v.Type()) - p.Elem().Set(v) - f32 := p.Convert(reflect.TypeOf((*float32)(nil))).Elem().Interface().(float32) - return f32 -} |
