diff options
Diffstat (limited to 'vendor/github.com/fxamacker/cbor/v2/decode.go')
-rw-r--r-- | vendor/github.com/fxamacker/cbor/v2/decode.go | 1642 |
1 files changed, 1642 insertions, 0 deletions
diff --git a/vendor/github.com/fxamacker/cbor/v2/decode.go b/vendor/github.com/fxamacker/cbor/v2/decode.go new file mode 100644 index 0000000000..079e682131 --- /dev/null +++ b/vendor/github.com/fxamacker/cbor/v2/decode.go @@ -0,0 +1,1642 @@ +// Copyright (c) Faye Amacker. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +package cbor + +import ( + "encoding" + "encoding/binary" + "errors" + "fmt" + "io" + "math" + "reflect" + "strconv" + "strings" + "time" + "unicode/utf8" + + "github.com/x448/float16" +) + +// Unmarshal parses the CBOR-encoded data and stores the result in the value +// pointed to by v using the default decoding options. If v is nil or not a +// pointer, Unmarshal returns an error. +// +// Unmarshal uses the inverse of the encodings that Marshal uses, allocating +// maps, slices, and pointers as necessary, with the following additional rules: +// +// To unmarshal CBOR into a pointer, Unmarshal first handles the case of the +// CBOR being the CBOR literal null. In that case, Unmarshal sets the pointer +// to nil. Otherwise, Unmarshal unmarshals the CBOR into the value pointed at +// by the pointer. If the pointer is nil, Unmarshal allocates a new value for +// it to point to. +// +// To unmarshal CBOR into an interface value, Unmarshal stores one of these in +// the interface value: +// +// bool, for CBOR booleans +// uint64, for CBOR positive integers +// int64, for CBOR negative integers +// float64, for CBOR floating points +// []byte, for CBOR byte strings +// string, for CBOR text strings +// []interface{}, for CBOR arrays +// map[interface{}]interface{}, for CBOR maps +// nil, for CBOR null +// +// To unmarshal a CBOR array into a slice, Unmarshal allocates a new slice only +// if the CBOR array is empty or slice capacity is less than CBOR array length. +// Otherwise Unmarshal reuses the existing slice, overwriting existing elements. +// Unmarshal sets the slice length to CBOR array length. +// +// To ummarshal a CBOR array into a Go array, Unmarshal decodes CBOR array +// elements into corresponding Go array elements. If the Go array is smaller +// than the CBOR array, the additional CBOR array elements are discarded. If +// the CBOR array is smaller than the Go array, the additional Go array elements +// are set to zero values. +// +// To unmarshal a CBOR map into a map, Unmarshal allocates a new map only if the +// map is nil. Otherwise Unmarshal reuses the existing map, keeping existing +// entries. Unmarshal stores key-value pairs from the CBOR map into Go map. +// +// To unmarshal a CBOR map into a struct, Unmarshal matches CBOR map keys to the +// keys in the following priority: +// +// 1. "cbor" key in struct field tag, +// 2. "json" key in struct field tag, +// 3. struct field name. +// +// Unmarshal prefers an exact match but also accepts a case-insensitive match. +// Map keys which don't have a corresponding struct field are ignored. +// +// To unmarshal a CBOR text string into a time.Time value, Unmarshal parses text +// string formatted in RFC3339. To unmarshal a CBOR integer/float into a +// time.Time value, Unmarshal creates an unix time with integer/float as seconds +// and fractional seconds since January 1, 1970 UTC. +// +// To unmarshal CBOR into a value implementing the Unmarshaler interface, +// Unmarshal calls that value's UnmarshalCBOR method. +// +// Unmarshal decodes a CBOR byte string into a value implementing +// encoding.BinaryUnmarshaler. +// +// If a CBOR value is not appropriate for a given Go type, or if a CBOR number +// overflows the Go type, Unmarshal skips that field and completes the +// unmarshalling as best as it can. If no more serious errors are encountered, +// unmarshal returns an UnmarshalTypeError describing the earliest such error. +// In any case, it's not guaranteed that all the remaining fields following the +// problematic one will be unmarshaled into the target object. +// +// The CBOR null value unmarshals into a slice/map/pointer/interface by setting +// that Go value to nil. Because null is often used to mean "not present", +// unmarshalling a CBOR null into any other Go type has no effect on the value +// produces no error. +// +// Unmarshal ignores CBOR tag data and parses tagged data following CBOR tag. +func Unmarshal(data []byte, v interface{}) error { + return defaultDecMode.Unmarshal(data, v) +} + +// Unmarshaler is the interface implemented by types that can unmarshal a CBOR +// representation of themselves. The input can be assumed to be a valid encoding +// of a CBOR value. UnmarshalCBOR must copy the CBOR data if it wishes to retain +// the data after returning. +type Unmarshaler interface { + UnmarshalCBOR([]byte) error +} + +// InvalidUnmarshalError describes an invalid argument passed to Unmarshal. +type InvalidUnmarshalError struct { + Type reflect.Type +} + +func (e *InvalidUnmarshalError) Error() string { + if e.Type == nil { + return "cbor: Unmarshal(nil)" + } + if e.Type.Kind() != reflect.Ptr { + return "cbor: Unmarshal(non-pointer " + e.Type.String() + ")" + } + return "cbor: Unmarshal(nil " + e.Type.String() + ")" +} + +// UnmarshalTypeError describes a CBOR value that was not appropriate for a Go type. +type UnmarshalTypeError struct { + Value string // description of CBOR value + Type reflect.Type // type of Go value it could not be assigned to + Struct string // struct type containing the field + Field string // name of the field holding the Go value + errMsg string // additional error message (optional) +} + +func (e *UnmarshalTypeError) Error() string { + var s string + if e.Struct != "" || e.Field != "" { + s = "cbor: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String() + } else { + s = "cbor: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() + } + if e.errMsg != "" { + s += " (" + e.errMsg + ")" + } + return s +} + +// DupMapKeyError describes detected duplicate map key in CBOR map. +type DupMapKeyError struct { + Key interface{} + Index int +} + +func (e *DupMapKeyError) Error() string { + return fmt.Sprintf("cbor: found duplicate map key \"%v\" at map element index %d", e.Key, e.Index) +} + +// DupMapKeyMode specifies how to enforce duplicate map key. +type DupMapKeyMode int + +const ( + // DupMapKeyQuiet doesn't enforce duplicate map key. Decoder quietly (no error) + // uses faster of "keep first" or "keep last" depending on Go data type and other factors. + DupMapKeyQuiet DupMapKeyMode = iota + + // DupMapKeyEnforcedAPF enforces detection and rejection of duplicate map keys. + // APF means "Allow Partial Fill" and the destination map or struct can be partially filled. + // If a duplicate map key is detected, DupMapKeyError is returned without further decoding + // of the map. It's the caller's responsibility to respond to DupMapKeyError by + // discarding the partially filled result if their protocol requires it. + // WARNING: using DupMapKeyEnforcedAPF will decrease performance and increase memory use. + DupMapKeyEnforcedAPF + + maxDupMapKeyMode +) + +func (dmkm DupMapKeyMode) valid() bool { + return dmkm < maxDupMapKeyMode +} + +// IndefLengthMode specifies whether to allow indefinite length items. +type IndefLengthMode int + +const ( + // IndefLengthAllowed allows indefinite length items. + IndefLengthAllowed IndefLengthMode = iota + + // IndefLengthForbidden disallows indefinite length items. + IndefLengthForbidden + + maxIndefLengthMode +) + +func (m IndefLengthMode) valid() bool { + return m < maxIndefLengthMode +} + +// TagsMode specifies whether to allow CBOR tags. +type TagsMode int + +const ( + // TagsAllowed allows CBOR tags. + TagsAllowed TagsMode = iota + + // TagsForbidden disallows CBOR tags. + TagsForbidden + + maxTagsMode +) + +func (tm TagsMode) valid() bool { + return tm < maxTagsMode +} + +// DecOptions specifies decoding options. +type DecOptions struct { + // DupMapKey specifies whether to enforce duplicate map key. + DupMapKey DupMapKeyMode + + // TimeTag specifies whether to check validity of time.Time (e.g. valid tag number and tag content type). + // For now, valid tag number means 0 or 1 as specified in RFC 7049 if the Go type is time.Time. + TimeTag DecTagMode + + // MaxNestedLevels specifies the max nested levels allowed for any combination of CBOR array, maps, and tags. + // Default is 32 levels and it can be set to [4, 256]. + MaxNestedLevels int + + // MaxArrayElements specifies the max number of elements for CBOR arrays. + // Default is 128*1024=131072 and it can be set to [16, 134217728] + MaxArrayElements int + + // MaxMapPairs specifies the max number of key-value pairs for CBOR maps. + // Default is 128*1024=131072 and it can be set to [16, 134217728] + MaxMapPairs int + + // IndefLength specifies whether to allow indefinite length CBOR items. + IndefLength IndefLengthMode + + // TagsMd specifies whether to allow CBOR tags (major type 6). + TagsMd TagsMode +} + +// DecMode returns DecMode with immutable options and no tags (safe for concurrency). +func (opts DecOptions) DecMode() (DecMode, error) { + return opts.decMode() +} + +// DecModeWithTags returns DecMode with options and tags that are both immutable (safe for concurrency). +func (opts DecOptions) DecModeWithTags(tags TagSet) (DecMode, error) { + if opts.TagsMd == TagsForbidden { + return nil, errors.New("cbor: cannot create DecMode with TagSet when TagsMd is TagsForbidden") + } + if tags == nil { + return nil, errors.New("cbor: cannot create DecMode with nil value as TagSet") + } + + dm, err := opts.decMode() + 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.DecTag != DecTagIgnored { + ts[contentType] = tag + } + } + syncTags.RUnlock() + + if len(ts) > 0 { + dm.tags = ts + } + + return dm, nil +} + +// DecModeWithSharedTags returns DecMode with immutable options and mutable shared tags (safe for concurrency). +func (opts DecOptions) DecModeWithSharedTags(tags TagSet) (DecMode, error) { + if opts.TagsMd == TagsForbidden { + return nil, errors.New("cbor: cannot create DecMode with TagSet when TagsMd is TagsForbidden") + } + if tags == nil { + return nil, errors.New("cbor: cannot create DecMode with nil value as TagSet") + } + dm, err := opts.decMode() + if err != nil { + return nil, err + } + dm.tags = tags + return dm, nil +} + +const ( + defaultMaxArrayElements = 131072 + minMaxArrayElements = 16 + maxMaxArrayElements = 134217728 + + defaultMaxMapPairs = 131072 + minMaxMapPairs = 16 + maxMaxMapPairs = 134217728 +) + +func (opts DecOptions) decMode() (*decMode, error) { + if !opts.DupMapKey.valid() { + return nil, errors.New("cbor: invalid DupMapKey " + strconv.Itoa(int(opts.DupMapKey))) + } + 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.TagsMd.valid() { + return nil, errors.New("cbor: invalid TagsMd " + strconv.Itoa(int(opts.TagsMd))) + } + if opts.MaxNestedLevels == 0 { + opts.MaxNestedLevels = 32 + } else if opts.MaxNestedLevels < 4 || opts.MaxNestedLevels > 256 { + return nil, errors.New("cbor: invalid MaxNestedLevels " + strconv.Itoa(opts.MaxNestedLevels) + " (range is [4, 256])") + } + if opts.MaxArrayElements == 0 { + opts.MaxArrayElements = defaultMaxArrayElements + } else if opts.MaxArrayElements < minMaxArrayElements || opts.MaxArrayElements > maxMaxArrayElements { + return nil, errors.New("cbor: invalid MaxArrayElements " + strconv.Itoa(opts.MaxArrayElements) + " (range is [" + strconv.Itoa(minMaxArrayElements) + ", " + strconv.Itoa(maxMaxArrayElements) + "])") + } + if opts.MaxMapPairs == 0 { + opts.MaxMapPairs = defaultMaxMapPairs + } else if opts.MaxMapPairs < minMaxMapPairs || opts.MaxMapPairs > maxMaxMapPairs { + return nil, errors.New("cbor: invalid MaxMapPairs " + strconv.Itoa(opts.MaxMapPairs) + " (range is [" + strconv.Itoa(minMaxMapPairs) + ", " + strconv.Itoa(maxMaxMapPairs) + "])") + } + dm := decMode{ + dupMapKey: opts.DupMapKey, + timeTag: opts.TimeTag, + maxNestedLevels: opts.MaxNestedLevels, + maxArrayElements: opts.MaxArrayElements, + maxMapPairs: opts.MaxMapPairs, + indefLength: opts.IndefLength, + tagsMd: opts.TagsMd, + } + return &dm, nil +} + +// DecMode is the main interface for CBOR decoding. +type DecMode interface { + Unmarshal(data []byte, v interface{}) error + NewDecoder(r io.Reader) *Decoder + DecOptions() DecOptions +} + +type decMode struct { + tags tagProvider + dupMapKey DupMapKeyMode + timeTag DecTagMode + maxNestedLevels int + maxArrayElements int + maxMapPairs int + indefLength IndefLengthMode + tagsMd TagsMode +} + +var defaultDecMode, _ = DecOptions{}.decMode() + +// DecOptions returns user specified options used to create this DecMode. +func (dm *decMode) DecOptions() DecOptions { + return DecOptions{ + DupMapKey: dm.dupMapKey, + TimeTag: dm.timeTag, + MaxNestedLevels: dm.maxNestedLevels, + MaxArrayElements: dm.maxArrayElements, + MaxMapPairs: dm.maxMapPairs, + IndefLength: dm.indefLength, + TagsMd: dm.tagsMd, + } +} + +// Unmarshal parses the CBOR-encoded data and stores the result in the value +// pointed to by v using dm DecMode. If v is nil or not a pointer, Unmarshal +// returns an error. +// +// See the documentation for Unmarshal for details. +func (dm *decMode) Unmarshal(data []byte, v interface{}) error { + d := decodeState{data: data, dm: dm} + return d.value(v) +} + +// NewDecoder returns a new decoder that reads from r using dm DecMode. +func (dm *decMode) NewDecoder(r io.Reader) *Decoder { + return &Decoder{r: r, d: decodeState{dm: dm}} +} + +type decodeState struct { + data []byte + off int // next read offset in data + dm *decMode +} + +func (d *decodeState) value(v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Ptr || rv.IsNil() { + return &InvalidUnmarshalError{reflect.TypeOf(v)} + } + + off := d.off // Save offset before data validation + err := d.valid() + d.off = off // Restore offset + if err != nil { + return err + } + + rv = rv.Elem() + + if rv.Kind() == reflect.Interface && rv.NumMethod() == 0 { + // Fast path to decode to empty interface without retrieving typeInfo. + iv, err := d.parse() + if iv != nil { + rv.Set(reflect.ValueOf(iv)) + } + return err + } + + return d.parseToValue(rv, getTypeInfo(rv.Type())) +} + +type cborType uint8 + +const ( + cborTypePositiveInt cborType = 0x00 + cborTypeNegativeInt cborType = 0x20 + cborTypeByteString cborType = 0x40 + cborTypeTextString cborType = 0x60 + cborTypeArray cborType = 0x80 + cborTypeMap cborType = 0xa0 + cborTypeTag cborType = 0xc0 + cborTypePrimitives cborType = 0xe0 +) + +func (t cborType) String() string { + switch t { + case cborTypePositiveInt: + return "positive integer" + case cborTypeNegativeInt: + return "negative integer" + case cborTypeByteString: + return "byte string" + case cborTypeTextString: + return "UTF-8 text string" + case cborTypeArray: + return "array" + case cborTypeMap: + return "map" + case cborTypeTag: + return "tag" + case cborTypePrimitives: + return "primitives" + default: + return "Invalid type " + strconv.Itoa(int(t)) + } +} + +// parseToValue assumes data is well-formed, and does not perform bounds checking. +// This function is complicated because it's the main function that decodes CBOR data to reflect.Value. +func (d *decodeState) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo + // Create new value for the pointer v to point to if CBOR value is not nil/undefined. + if !d.nextCBORNil() { + for v.Kind() == reflect.Ptr { + if v.IsNil() { + if !v.CanSet() { + d.skip() + return errors.New("cbor: cannot set new value for " + v.Type().String()) + } + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + } + + if tInfo.spclType != specialTypeNone { + switch tInfo.spclType { + case specialTypeEmptyIface: + iv, err := d.parse() + if iv != nil { + v.Set(reflect.ValueOf(iv)) + } + return err + case specialTypeTag: + return d.parseToTag(v) + case specialTypeTime: + return d.parseToTime(v) + case specialTypeUnmarshalerIface: + return d.parseToUnmarshaler(v) + } + } + + // Check registered tag number + if tagItem := d.getRegisteredTagItem(tInfo.nonPtrType); tagItem != nil { + t := d.nextCBORType() + if t != cborTypeTag { + if tagItem.opts.DecTag == DecTagRequired { + d.skip() // Required tag number is absent, skip entire tag + return &UnmarshalTypeError{Value: t.String(), Type: tInfo.typ, errMsg: "expect CBOR tag value"} + } + } else if err := d.validRegisteredTagNums(tInfo.nonPtrType, tagItem.num); err != nil { + d.skip() // Skip tag content + return err + } + } + + t := d.nextCBORType() + + // Skip tag number(s) here to avoid recursion + if t == cborTypeTag { + d.getHead() + t = d.nextCBORType() + for t == cborTypeTag { + d.getHead() + t = d.nextCBORType() + } + } + + switch t { + case cborTypePositiveInt: + _, _, val := d.getHead() + return fillPositiveInt(t, val, v) + case cborTypeNegativeInt: + _, _, val := d.getHead() + if val > math.MaxInt64 { + return &UnmarshalTypeError{ + Value: t.String(), + Type: tInfo.nonPtrType, + errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64", + } + } + nValue := int64(-1) ^ int64(val) + return fillNegativeInt(t, nValue, v) + case cborTypeByteString: + b := d.parseByteString() + return fillByteString(t, b, v) + case cborTypeTextString: + b, err := d.parseTextString() + if err != nil { + return err + } + return fillTextString(t, b, v) + case cborTypePrimitives: + _, ai, val := d.getHead() + if ai < 20 || ai == 24 { + return fillPositiveInt(t, val, v) + } + switch ai { + case 20, 21: + return fillBool(t, ai == 21, v) + case 22, 23: + return fillNil(t, v) + case 25: + f := float64(float16.Frombits(uint16(val)).Float32()) + return fillFloat(t, f, v) + case 26: + f := float64(math.Float32frombits(uint32(val))) + return fillFloat(t, f, v) + case 27: + f := math.Float64frombits(val) + return fillFloat(t, f, v) + } + case cborTypeArray: + if tInfo.nonPtrKind == reflect.Slice { + return d.parseArrayToSlice(v, tInfo) + } else if tInfo.nonPtrKind == reflect.Array { + return d.parseArrayToArray(v, tInfo) + } else if tInfo.nonPtrKind == reflect.Struct { + return d.parseArrayToStruct(v, tInfo) + } + d.skip() + return &UnmarshalTypeError{Value: t.String(), Type: tInfo.nonPtrType} + case cborTypeMap: + if tInfo.nonPtrKind == reflect.Struct { + return d.parseMapToStruct(v, tInfo) + } else if tInfo.nonPtrKind == reflect.Map { + return d.parseMapToMap(v, tInfo) + } + d.skip() + return &UnmarshalTypeError{Value: t.String(), Type: tInfo.nonPtrType} + } + return nil +} + +func (d *decodeState) parseToTag(v reflect.Value) error { + t := d.nextCBORType() + if t != cborTypeTag { + d.skip() + return &UnmarshalTypeError{Value: t.String(), Type: typeTag} + } + + // Unmarshal tag number + _, _, num := d.getHead() + + // Unmarshal tag content + content, err := d.parse() + if err != nil { + return err + } + + v.Set(reflect.ValueOf(Tag{num, content})) + return nil +} + +func (d *decodeState) parseToTime(v reflect.Value) error { + t := d.nextCBORType() + + // Verify that tag number or absent of tag number is acceptable to specified timeTag. + if t == cborTypeTag { + if d.dm.timeTag == DecTagIgnored { + // Skip tag number + d.getHead() + t = d.nextCBORType() + for t == cborTypeTag { + d.getHead() + t = d.nextCBORType() + } + } else { + // Read tag number + _, _, tagNum := d.getHead() + + // Verify tag number (0 or 1) is followed by appropriate tag content type. + t = d.nextCBORType() + switch tagNum { + case 0: + // Tag content (date/time text string in RFC 3339 format) must be string type. + if t != cborTypeTextString { + d.skip() + return errors.New("cbor: tag number 0 must be followed by text string, got " + t.String()) + } + case 1: + // Tag content (epoch date/time) must be uint, int, or float type. + if t != cborTypePositiveInt && t != cborTypeNegativeInt && (d.data[d.off] < 0xf9 || d.data[d.off] > 0xfb) { + d.skip() + return errors.New("cbor: tag number 1 must be followed by integer or floating-point number, got " + t.String()) + } + default: + d.skip() + return errors.New("cbor: wrong tag number for time.Time, got " + strconv.Itoa(int(tagNum)) + ", expect 0 or 1") + } + } + } else { + if d.dm.timeTag == DecTagRequired { + d.skip() + return &UnmarshalTypeError{Value: t.String(), Type: typeTime, errMsg: "expect CBOR tag value"} + } + } + + switch t { + case cborTypePositiveInt: + _, _, val := d.getHead() + tm := time.Unix(int64(val), 0) + v.Set(reflect.ValueOf(tm)) + return nil + case cborTypeNegativeInt: + _, _, val := d.getHead() + if val > math.MaxInt64 { + return &UnmarshalTypeError{ + Value: t.String(), + Type: typeTime, + errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64", + } + } + nValue := int64(-1) ^ int64(val) + tm := time.Unix(nValue, 0) + v.Set(reflect.ValueOf(tm)) + return nil + case cborTypeTextString: + b, err := d.parseTextString() + if err != nil { + return err + } + tm, err := time.Parse(time.RFC3339, string(b)) + if err != nil { + return errors.New("cbor: cannot set " + string(b) + " for time.Time: " + err.Error()) + } + v.Set(reflect.ValueOf(tm)) + return nil + case cborTypePrimitives: + _, ai, val := d.getHead() + var f float64 + switch ai { + case 22, 23: + v.Set(reflect.ValueOf(time.Time{})) + return nil + case 25: + f = float64(float16.Frombits(uint16(val)).Float32()) + case 26: + f = float64(math.Float32frombits(uint32(val))) + case 27: + f = math.Float64frombits(val) + default: + return &UnmarshalTypeError{Value: t.String(), Type: typeTime} + } + if math.IsNaN(f) || math.IsInf(f, 0) { + v.Set(reflect.ValueOf(time.Time{})) + return nil + } + f1, f2 := math.Modf(f) + tm := time.Unix(int64(f1), int64(f2*1e9)) + v.Set(reflect.ValueOf(tm)) + return nil + } + d.skip() + return &UnmarshalTypeError{Value: t.String(), Type: typeTime} +} + +// parseToUnmarshaler assumes data is well-formed, and does not perform bounds checking. +func (d *decodeState) parseToUnmarshaler(v reflect.Value) error { + if d.nextCBORNil() && v.Kind() == reflect.Ptr && v.IsNil() { + d.skip() + return nil + } + + if v.Kind() != reflect.Ptr && v.CanAddr() { + v = v.Addr() + } + if u, ok := v.Interface().(Unmarshaler); ok { + start := d.off + d.skip() + return u.UnmarshalCBOR(d.data[start:d.off]) + } + d.skip() + return errors.New("cbor: failed to assert " + v.Type().String() + " as cbor.Unmarshaler") +} + +// parse assumes data is well-formed, and does not perform bounds checking. +func (d *decodeState) parse() (interface{}, error) { + t := d.nextCBORType() + switch t { + case cborTypePositiveInt: + _, _, val := d.getHead() + return val, nil + case cborTypeNegativeInt: + _, _, val := d.getHead() + if val > math.MaxInt64 { + return nil, &UnmarshalTypeError{ + Value: t.String(), + Type: reflect.TypeOf([]interface{}(nil)).Elem(), + errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64", + } + } + nValue := int64(-1) ^ int64(val) + return nValue, nil + case cborTypeByteString: + return d.parseByteString(), nil + case cborTypeTextString: + b, err := d.parseTextString() + if err != nil { + return nil, err + } + return string(b), nil + case cborTypeTag: + _, _, tagNum := d.getHead() + nt := d.nextCBORType() + content, err := d.parse() + if err != nil { + return nil, err + } + switch tagNum { + case 0: + // Tag content should be date/time text string in RFC 3339 format. + s, ok := content.(string) + if !ok { + return nil, errors.New("cbor: tag number 0 must be followed by text string, got " + nt.String()) + } + tm, err := time.Parse(time.RFC3339, s) + if err != nil { + return nil, errors.New("cbor: cannot set " + s + " for time.Time: " + err.Error()) + } + return tm, nil + case 1: + // Tag content should be epoch date/time. + switch content := content.(type) { + case uint64: + return time.Unix(int64(content), 0), nil + case int64: + return time.Unix(content, 0), nil + case float64: + f1, f2 := math.Modf(content) + return time.Unix(int64(f1), int64(f2*1e9)), nil + default: + return nil, errors.New("cbor: tag number 1 must be followed by integer or floating-point number, got " + nt.String()) + } + } + return Tag{tagNum, content}, nil + case cborTypePrimitives: + _, ai, val := d.getHead() + if ai < 20 || ai == 24 { + return val, nil + } + switch ai { + case 20, 21: + return (ai == 21), nil + case 22, 23: + return nil, nil + case 25: + f := float64(float16.Frombits(uint16(val)).Float32()) + return f, nil + case 26: + f := float64(math.Float32frombits(uint32(val))) + return f, nil + case 27: + f := math.Float64frombits(val) + return f, nil + } + case cborTypeArray: + return d.parseArray() + case cborTypeMap: + return d.parseMap() + } + return nil, nil +} + +// parseByteString parses CBOR encoded byte string. It returns a byte slice +// pointing to a copy of parsed data. +func (d *decodeState) parseByteString() []byte { + _, ai, val := d.getHead() + if ai != 31 { + b := make([]byte, int(val)) + copy(b, d.data[d.off:d.off+int(val)]) + d.off += int(val) + return b + } + // Process indefinite length string chunks. + b := []byte{} + for !d.foundBreak() { + _, _, val = d.getHead() + b = append(b, d.data[d.off:d.off+int(val)]...) + d.off += int(val) + } + return b +} + +// parseTextString parses CBOR encoded text string. It does not return a string +// to prevent creating an extra copy of string. Caller should wrap returned +// byte slice as string when needed. +// +// parseStruct() uses parseTextString() to improve memory and performance, +// compared with using parse(reflect.Value). parse(reflect.Value) sets +// reflect.Value with parsed string, while parseTextString() returns zero-copy []byte. +func (d *decodeState) parseTextString() ([]byte, error) { + _, ai, val := d.getHead() + if ai != 31 { + b := d.data[d.off : d.off+int(val)] + d.off += int(val) + if !utf8.Valid(b) { + return nil, &SemanticError{"cbor: invalid UTF-8 string"} + } + return b, nil + } + // Process indefinite length string chunks. + b := []byte{} + for !d.foundBreak() { + _, _, val = d.getHead() + x := d.data[d.off : d.off+int(val)] + d.off += int(val) + if !utf8.Valid(x) { + for !d.foundBreak() { + d.skip() // Skip remaining chunk on error + } + return nil, &SemanticError{"cbor: invalid UTF-8 string"} + } + b = append(b, x...) + } + return b, nil +} + +func (d *decodeState) parseArray() ([]interface{}, error) { + _, ai, val := d.getHead() + hasSize := (ai != 31) + count := int(val) + if !hasSize { + count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance + } + v := make([]interface{}, count) + var e interface{} + var err, lastErr error + for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + if e, lastErr = d.parse(); lastErr != nil { + if err == nil { + err = lastErr + } + continue + } + v[i] = e + } + return v, err +} + +func (d *decodeState) parseArrayToSlice(v reflect.Value, tInfo *typeInfo) error { + _, ai, val := d.getHead() + hasSize := (ai != 31) + count := int(val) + if !hasSize { + count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance + } + if count == 0 { + v.Set(reflect.MakeSlice(tInfo.nonPtrType, 0, 0)) + } + if v.IsNil() || v.Cap() < count { + v.Set(reflect.MakeSlice(tInfo.nonPtrType, count, count)) + } + v.SetLen(count) + var err error + for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + if lastErr := d.parseToValue(v.Index(i), tInfo.elemTypeInfo); lastErr != nil { + if err == nil { + err = lastErr + } + } + } + return err +} + +func (d *decodeState) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error { + _, ai, val := d.getHead() + hasSize := (ai != 31) + count := int(val) + gi := 0 + vLen := v.Len() + var err error + for ci := 0; (hasSize && ci < count) || (!hasSize && !d.foundBreak()); ci++ { + if gi < vLen { + // Read CBOR array element and set array element + if lastErr := d.parseToValue(v.Index(gi), tInfo.elemTypeInfo); lastErr != nil { + if err == nil { + err = lastErr + } + } + gi++ + } else { + d.skip() // Skip remaining CBOR array element + } + } + // Set remaining Go array elements to zero values. + if gi < vLen { + zeroV := reflect.Zero(tInfo.elemTypeInfo.typ) + for ; gi < vLen; gi++ { + v.Index(gi).Set(zeroV) + } + } + return err +} + +func (d *decodeState) parseMap() (map[interface{}]interface{}, error) { + _, ai, val := d.getHead() + hasSize := (ai != 31) + count := int(val) + m := make(map[interface{}]interface{}) + var k, e interface{} + var err, lastErr error + keyCount := 0 + for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + // Parse CBOR map key. + if k, lastErr = d.parse(); lastErr != nil { + if err == nil { + err = lastErr + } + d.skip() + continue + } + + // Detect if CBOR map key can be used as Go map key. + kkind := reflect.ValueOf(k).Kind() + if tag, ok := k.(Tag); ok { + kkind = tag.contentKind() + } + if !isHashableKind(kkind) { + if err == nil { + err = errors.New("cbor: invalid map key type: " + kkind.String()) + } + d.skip() + continue + } + + // Parse CBOR map value. + if e, lastErr = d.parse(); lastErr != nil { + if err == nil { + err = lastErr + } + continue + } + + // Add key-value pair to Go map. + m[k] = e + + // Detect duplicate map key. + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + newKeyCount := len(m) + if newKeyCount == keyCount { + m[k] = nil + err = &DupMapKeyError{k, i} + i++ + // skip the rest of the map + for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + d.skip() // Skip map key + d.skip() // Skip map value + } + return m, err + } + keyCount = newKeyCount + } + } + return m, err +} + +func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo + _, ai, val := d.getHead() + hasSize := (ai != 31) + count := int(val) + if v.IsNil() { + mapsize := count + if !hasSize { + mapsize = 0 + } + v.Set(reflect.MakeMapWithSize(tInfo.nonPtrType, mapsize)) + } + keyType, eleType := tInfo.keyTypeInfo.typ, tInfo.elemTypeInfo.typ + reuseKey, reuseEle := isImmutableKind(tInfo.keyTypeInfo.kind), isImmutableKind(tInfo.elemTypeInfo.kind) + var keyValue, eleValue, zeroKeyValue, zeroEleValue reflect.Value + keyIsInterfaceType := keyType == typeIntf // If key type is interface{}, need to check if key value is hashable. + var err, lastErr error + keyCount := v.Len() + var existingKeys map[interface{}]bool // Store existing map keys, used for detecting duplicate map key. + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + existingKeys = make(map[interface{}]bool, keyCount) + if keyCount > 0 { + vKeys := v.MapKeys() + for i := 0; i < len(vKeys); i++ { + existingKeys[vKeys[i].Interface()] = true + } + } + } + for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + // Parse CBOR map key. + if !keyValue.IsValid() { + keyValue = reflect.New(keyType).Elem() + } else if !reuseKey { + if !zeroKeyValue.IsValid() { + zeroKeyValue = reflect.Zero(keyType) + } + keyValue.Set(zeroKeyValue) + } + if lastErr = d.parseToValue(keyValue, tInfo.keyTypeInfo); lastErr != nil { + if err == nil { + err = lastErr + } + d.skip() + continue + } + + // Detect if CBOR map key can be used as Go map key. + if keyIsInterfaceType { + kkind := keyValue.Elem().Kind() + if keyValue.Elem().IsValid() { + if tag, ok := keyValue.Elem().Interface().(Tag); ok { + kkind = tag.contentKind() + } + } + if !isHashableKind(kkind) { + if err == nil { + err = errors.New("cbor: invalid map key type: " + kkind.String()) + } + d.skip() + continue + } + } + + // Parse CBOR map value. + if !eleValue.IsValid() { + eleValue = reflect.New(eleType).Elem() + } else if !reuseEle { + if !zeroEleValue.IsValid() { + zeroEleValue = reflect.Zero(eleType) + } + eleValue.Set(zeroEleValue) + } + if lastErr := d.parseToValue(eleValue, tInfo.elemTypeInfo); lastErr != nil { + if err == nil { + err = lastErr + } + continue + } + + // Add key-value pair to Go map. + v.SetMapIndex(keyValue, eleValue) + + // Detect duplicate map key. + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + newKeyCount := v.Len() + if newKeyCount == keyCount { + kvi := keyValue.Interface() + if !existingKeys[kvi] { + v.SetMapIndex(keyValue, reflect.New(eleType).Elem()) + err = &DupMapKeyError{kvi, i} + i++ + // skip the rest of the map + for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + d.skip() // skip map key + d.skip() // skip map value + } + return err + } + delete(existingKeys, kvi) + } + keyCount = newKeyCount + } + } + return err +} + +func (d *decodeState) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error { + structType := getDecodingStructType(tInfo.nonPtrType) + if structType.err != nil { + return structType.err + } + + if !structType.toArray { + t := d.nextCBORType() + d.skip() + return &UnmarshalTypeError{ + Value: t.String(), + Type: tInfo.nonPtrType, + errMsg: "cannot decode CBOR array to struct without toarray option", + } + } + + start := d.off + t, ai, val := d.getHead() + hasSize := (ai != 31) + count := int(val) + if !hasSize { + count = d.numOfItemsUntilBreak() // peek ahead to get array size + } + if count != len(structType.fields) { + d.off = start + d.skip() + return &UnmarshalTypeError{ + Value: t.String(), + Type: tInfo.typ, + errMsg: "cannot decode CBOR array to struct with different number of elements", + } + } + var err error + for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ { + f := structType.fields[i] + fv, lastErr := fieldByIndex(v, f.idx) + if lastErr != nil { + if err == nil { + err = lastErr + } + d.skip() + continue + } + if lastErr := d.parseToValue(fv, f.typInfo); lastErr != nil { + if err == nil { + if typeError, ok := lastErr.(*UnmarshalTypeError); ok { + typeError.Struct = tInfo.typ.String() + typeError.Field = f.name + err = typeError + } else { + err = lastErr + } + } + } + } + return err +} + +// parseMapToStruct needs to be fast so gocyclo can be ignored for now. +func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo + structType := getDecodingStructType(tInfo.nonPtrType) + if structType.err != nil { + return structType.err + } + + if structType.toArray { + t := d.nextCBORType() + d.skip() + return &UnmarshalTypeError{ + Value: t.String(), + Type: tInfo.nonPtrType, + errMsg: "cannot decode CBOR map to struct with toarray option", + } + } + + foundFldIdx := make([]bool, len(structType.fields)) + _, ai, val := d.getHead() + hasSize := (ai != 31) + count := int(val) + var err, lastErr error + keyCount := 0 + var mapKeys map[interface{}]struct{} // Store map keys, used for detecting duplicate map key. + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + mapKeys = make(map[interface{}]struct{}, len(structType.fields)) + } + for j := 0; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { + var f *field + var k interface{} // Used by duplicate map key detection + + t := d.nextCBORType() + if t == cborTypeTextString { + var keyBytes []byte + keyBytes, lastErr = d.parseTextString() + if lastErr != nil { + if err == nil { + err = lastErr + } + d.skip() // skip value + continue + } + + keyLen := len(keyBytes) + // Find field with exact match + for i := 0; i < len(structType.fields); i++ { + fld := structType.fields[i] + if !foundFldIdx[i] && len(fld.name) == keyLen && fld.name == string(keyBytes) { + f = fld + foundFldIdx[i] = true + break + } + } + // Find field with case-insensitive match + if f == nil { + keyString := string(keyBytes) + for i := 0; i < len(structType.fields); i++ { + fld := structType.fields[i] + if !foundFldIdx[i] && len(fld.name) == keyLen && strings.EqualFold(fld.name, keyString) { + f = fld + foundFldIdx[i] = true + break + } + } + } + + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + k = string(keyBytes) + } + } else if t <= cborTypeNegativeInt { // uint/int + var nameAsInt int64 + + if t == cborTypePositiveInt { + _, _, val := d.getHead() + nameAsInt = int64(val) + } else { + _, _, val := d.getHead() + if val > math.MaxInt64 { + if err == nil { + err = &UnmarshalTypeError{ + Value: t.String(), + Type: reflect.TypeOf(int64(0)), + errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64", + } + } + d.skip() // skip value + continue + } + nameAsInt = int64(-1) ^ int64(val) + } + + // Find field + for i := 0; i < len(structType.fields); i++ { + fld := structType.fields[i] + if !foundFldIdx[i] && fld.keyAsInt && fld.nameAsInt == nameAsInt { + f = fld + foundFldIdx[i] = true + break + } + } + + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + k = nameAsInt + } + } else { + if err == nil { + err = &UnmarshalTypeError{ + Value: t.String(), + Type: reflect.TypeOf(""), + errMsg: "map key is of type " + t.String() + " and cannot be used to match struct field name", + } + } + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + // parse key + k, lastErr = d.parse() + if lastErr != nil { + d.skip() // skip value + continue + } + // Detect if CBOR map key can be used as Go map key. + kkind := reflect.ValueOf(k).Kind() + if tag, ok := k.(Tag); ok { + kkind = tag.contentKind() + } + if !isHashableKind(kkind) { + d.skip() // skip value + continue + } + } else { + d.skip() // skip key + } + } + + if d.dm.dupMapKey == DupMapKeyEnforcedAPF { + mapKeys[k] = struct{}{} + newKeyCount := len(mapKeys) + if newKeyCount == keyCount { + err = &DupMapKeyError{k, j} + d.skip() // skip value + j++ + // skip the rest of the map + for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ { + d.skip() + d.skip() + } + return err + } + keyCount = newKeyCount + } + + if f == nil { + d.skip() // Skip value + continue + } + // reflect.Value.FieldByIndex() panics at nil pointer to unexported + // anonymous field. fieldByIndex() returns error. + fv, lastErr := fieldByIndex(v, f.idx) + if lastErr != nil { + if err == nil { + err = lastErr + } + d.skip() + continue + } + if lastErr = d.parseToValue(fv, f.typInfo); lastErr != nil { + if err == nil { + if typeError, ok := lastErr.(*UnmarshalTypeError); ok { + typeError.Struct = tInfo.nonPtrType.String() + typeError.Field = f.name + err = typeError + } else { + err = lastErr + } + } + } + } + return err +} + +// validRegisteredTagNums verifies that tag numbers match registered tag numbers of type t. +// validRegisteredTagNums assumes next CBOR data type is tag. It scans all tag numbers, and stops at tag content. +func (d *decodeState) validRegisteredTagNums(t reflect.Type, registeredTagNums []uint64) error { + // Scan until next cbor data is tag content. + tagNums := make([]uint64, 0, 2) + for d.nextCBORType() == cborTypeTag { + _, _, val := d.getHead() + tagNums = append(tagNums, val) + } + + // Verify that tag numbers match registered tag numbers of type t + if len(tagNums) != len(registeredTagNums) { + return &WrongTagError{t, registeredTagNums, tagNums} + } + for i, n := range registeredTagNums { + if n != tagNums[i] { + return &WrongTagError{t, registeredTagNums, tagNums} + } + } + return nil +} + +func (d *decodeState) getRegisteredTagItem(vt reflect.Type) *tagItem { + if d.dm.tags != nil { + return d.dm.tags.get(vt) + } + return nil +} + +// skip moves data offset to the next item. skip assumes data is well-formed, +// and does not perform bounds checking. +func (d *decodeState) skip() { + t, ai, val := d.getHead() + + if ai == 31 { + switch t { + case cborTypeByteString, cborTypeTextString, cborTypeArray, cborTypeMap: + for { + if d.data[d.off] == 0xff { + d.off++ + return + } + d.skip() + } + } + } + + switch t { + case cborTypeByteString, cborTypeTextString: + d.off += int(val) + case cborTypeArray: + for i := 0; i < int(val); i++ { + d.skip() + } + case cborTypeMap: + for i := 0; i < int(val)*2; i++ { + d.skip() + } + case cborTypeTag: + d.skip() + } +} + +// getHead assumes data is well-formed, and does not perform bounds checking. +func (d *decodeState) getHead() (t cborType, ai byte, val uint64) { + t = cborType(d.data[d.off] & 0xe0) + ai = d.data[d.off] & 0x1f + val = uint64(ai) + d.off++ + + if ai < 24 { + return + } + if ai == 24 { + val = uint64(d.data[d.off]) + d.off++ + return + } + if ai == 25 { + val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2])) + d.off += 2 + return + } + if ai == 26 { + val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4])) + d.off += 4 + return + } + if ai == 27 { + val = binary.BigEndian.Uint64(d.data[d.off : d.off+8]) + d.off += 8 + return + } + return +} + +func (d *decodeState) numOfItemsUntilBreak() int { + savedOff := d.off + i := 0 + for !d.foundBreak() { + d.skip() + i++ + } + d.off = savedOff + return i +} + +// foundBreak assumes data is well-formed, and does not perform bounds checking. +func (d *decodeState) foundBreak() bool { + if d.data[d.off] == 0xff { + d.off++ + return true + } + return false +} + +func (d *decodeState) reset(data []byte) { + d.data = data + d.off = 0 +} + +func (d *decodeState) nextCBORType() cborType { + return cborType(d.data[d.off] & 0xe0) +} + +func (d *decodeState) nextCBORNil() bool { + return d.data[d.off] == 0xf6 || d.data[d.off] == 0xf7 +} + +var ( + typeIntf = reflect.TypeOf([]interface{}(nil)).Elem() + typeTime = reflect.TypeOf(time.Time{}) + typeUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + typeBinaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() +) + +func fillNil(t cborType, v reflect.Value) error { + switch v.Kind() { + case reflect.Slice, reflect.Map, reflect.Interface, reflect.Ptr: + v.Set(reflect.Zero(v.Type())) + return nil + } + return nil +} + +func fillPositiveInt(t cborType, val uint64, v reflect.Value) error { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if val > math.MaxInt64 { + return &UnmarshalTypeError{Value: t.String(), Type: v.Type(), errMsg: strconv.FormatUint(val, 10) + " overflows " + v.Type().String()} + } + if v.OverflowInt(int64(val)) { + return &UnmarshalTypeError{Value: t.String(), Type: v.Type(), errMsg: strconv.FormatUint(val, 10) + " overflows " + v.Type().String()} + } + v.SetInt(int64(val)) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if v.OverflowUint(val) { + return &UnmarshalTypeError{Value: t.String(), Type: v.Type(), errMsg: strconv.FormatUint(val, 10) + " overflows " + v.Type().String()} + } + v.SetUint(val) + return nil + case reflect.Float32, reflect.Float64: + f := float64(val) + v.SetFloat(f) + return nil + } + return &UnmarshalTypeError{Value: t.String(), Type: v.Type()} +} + +func fillNegativeInt(t cborType, val int64, v reflect.Value) error { + switch v.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + if v.OverflowInt(val) { + return &UnmarshalTypeError{Value: t.String(), Type: v.Type(), errMsg: strconv.FormatInt(val, 10) + " overflows " + v.Type().String()} + } + v.SetInt(val) + return nil + case reflect.Float32, reflect.Float64: + f := float64(val) + v.SetFloat(f) + return nil + } + return &UnmarshalTypeError{Value: t.String(), Type: v.Type()} +} + +func fillBool(t cborType, val bool, v reflect.Value) error { + if v.Kind() == reflect.Bool { + v.SetBool(val) + return nil + } + return &UnmarshalTypeError{Value: t.String(), Type: v.Type()} +} + +func fillFloat(t cborType, val float64, v reflect.Value) error { + switch v.Kind() { + case reflect.Float32, reflect.Float64: + if v.OverflowFloat(val) { + return &UnmarshalTypeError{ + Value: t.String(), + Type: v.Type(), + errMsg: strconv.FormatFloat(val, 'E', -1, 64) + " overflows " + v.Type().String(), + } + } + v.SetFloat(val) + return nil + } + return &UnmarshalTypeError{Value: t.String(), Type: v.Type()} +} + +func fillByteString(t cborType, val []byte, v reflect.Value) error { + if reflect.PtrTo(v.Type()).Implements(typeBinaryUnmarshaler) { + if v.CanAddr() { + v = v.Addr() + if u, ok := v.Interface().(encoding.BinaryUnmarshaler); ok { + return u.UnmarshalBinary(val) + } + } + return errors.New("cbor: cannot set new value for " + v.Type().String()) + } + if v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Uint8 { + v.SetBytes(val) + return nil + } + if v.Kind() == reflect.Array && v.Type().Elem().Kind() == reflect.Uint8 { + vLen := v.Len() + i := 0 + for ; i < vLen && i < len(val); i++ { + v.Index(i).SetUint(uint64(val[i])) + } + // Set remaining Go array elements to zero values. + if i < vLen { + zeroV := reflect.Zero(reflect.TypeOf(byte(0))) + for ; i < vLen; i++ { + v.Index(i).Set(zeroV) + } + } + return nil + } + return &UnmarshalTypeError{Value: t.String(), Type: v.Type()} +} + +func fillTextString(t cborType, val []byte, v reflect.Value) error { + if v.Kind() == reflect.String { + v.SetString(string(val)) + return nil + } + return &UnmarshalTypeError{Value: t.String(), Type: v.Type()} +} + +func isImmutableKind(k reflect.Kind) bool { + switch k { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64, + reflect.String: + return true + default: + return false + } +} + +func isHashableKind(k reflect.Kind) bool { + switch k { + case reflect.Slice, reflect.Map, reflect.Func: + return false + default: + return true + } +} + +// fieldByIndex returns the nested field corresponding to the index. It +// allocates pointer to struct field if it is nil and settable. +// reflect.Value.FieldByIndex() panics at nil pointer to unexported anonymous +// field. This function returns error. +func fieldByIndex(v reflect.Value, index []int) (reflect.Value, error) { + for _, i := range index { + if v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Struct { + if v.IsNil() { + if !v.CanSet() { + return reflect.Value{}, errors.New("cbor: cannot set embedded pointer to unexported struct: " + v.Type().String()) + } + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + v = v.Field(i) + } + return v, nil +} |