cbor/decode.go
Faye Amacker e882cb5e44
Add CBOR duplicate map key options (#152)
This CBOR library provides 2 options for duplicate map keys:

- DupMapKeyQuiet: Turn off detection of duplicate map keys. It tries
to use a "keep fastest" method by choosing either "keep first" or
"keep last" depending on the Go data type.

- DupMapKeyEnforcedAPF: Turn on detection and rejection of duplidate
map keys. Decoding stops immediately and returns DupMapKeyError when the
first duplicate key is detected. The error includes the duplicate map key
and the index number.

APF suffix means "Allow Partial Fill" so the destination map or struct
can contain some decoded values at the time of error. It is the caller's
responsibility to respond to the DuplicateMapKeyErr by discarding the
partially filled result if that's required by their protocol.

Detection of duplicate map keys relies on whether the CBOR map key would
be a duplicate "key" when decoded and applied to the user-provided Go map
or struct.
2020-02-16 21:49:31 -06:00

1517 lines
41 KiB
Go

// 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
}
// 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
}
// 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 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 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
}
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)))
}
dm := decMode{dupMapKey: opts.DupMapKey, timeTag: opts.TimeTag}
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
}
var defaultDecMode = &decMode{}
// DecOptions returns user specified options used to create this DecMode.
func (dm *decMode) DecOptions() DecOptions {
return DecOptions{DupMapKey: dm.dupMapKey, TimeTag: dm.timeTag}
}
// 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)}
}
if _, err := valid(d.data[d.off:]); 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{}
var err error
for !d.foundBreak() {
_, _, val = d.getHead()
x := d.data[d.off : d.off+int(val)]
d.off += int(val)
if !utf8.Valid(x) {
err = &SemanticError{"cbor: invalid UTF-8 string"}
break
}
b = append(b, x...)
}
if err != nil {
for !d.foundBreak() {
d.skip() // Skip remaining chunk on error
}
return nil, err
}
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)
i := 0
var err error
for ; i < v.Len() && ((hasSize && i < count) || (!hasSize && !d.foundBreak())); i++ {
if lastErr := d.parseToValue(v.Index(i), tInfo.elemTypeInfo); lastErr != nil {
if err == nil {
err = lastErr
}
}
}
// Set remaining Go array elements to zero values.
if i < v.Len() {
zeroV := reflect.Zero(tInfo.elemTypeInfo.typ)
for ; i < v.Len(); i++ {
v.Index(i).Set(zeroV)
}
}
// Skip remaining CBOR array elements
for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
d.skip()
}
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
}
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
}