gitea/vendor/github.com/fxamacker/cbor/v2/cache.go

309 lines
7.2 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 (
"bytes"
"errors"
"reflect"
"sort"
"strconv"
"strings"
"sync"
)
var (
decodingStructTypeCache sync.Map // map[reflect.Type]*decodingStructType
encodingStructTypeCache sync.Map // map[reflect.Type]*encodingStructType
encodeFuncCache sync.Map // map[reflect.Type]encodeFunc
typeInfoCache sync.Map // map[reflect.Type]*typeInfo
)
type specialType int
const (
specialTypeNone specialType = iota
specialTypeUnmarshalerIface
specialTypeEmptyIface
specialTypeTag
specialTypeTime
)
type typeInfo struct {
elemTypeInfo *typeInfo
keyTypeInfo *typeInfo
typ reflect.Type
kind reflect.Kind
nonPtrType reflect.Type
nonPtrKind reflect.Kind
spclType specialType
}
func newTypeInfo(t reflect.Type) *typeInfo {
tInfo := typeInfo{typ: t, kind: t.Kind()}
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
k := t.Kind()
tInfo.nonPtrType = t
tInfo.nonPtrKind = k
if k == reflect.Interface && t.NumMethod() == 0 {
tInfo.spclType = specialTypeEmptyIface
} else if t == typeTag {
tInfo.spclType = specialTypeTag
} else if t == typeTime {
tInfo.spclType = specialTypeTime
} else if reflect.PtrTo(t).Implements(typeUnmarshaler) {
tInfo.spclType = specialTypeUnmarshalerIface
}
switch k {
case reflect.Array, reflect.Slice:
tInfo.elemTypeInfo = getTypeInfo(t.Elem())
case reflect.Map:
tInfo.keyTypeInfo = getTypeInfo(t.Key())
tInfo.elemTypeInfo = getTypeInfo(t.Elem())
}
return &tInfo
}
type decodingStructType struct {
fields fields
err error
toArray bool
}
func getDecodingStructType(t reflect.Type) *decodingStructType {
if v, _ := decodingStructTypeCache.Load(t); v != nil {
return v.(*decodingStructType)
}
flds, structOptions := getFields(t)
toArray := hasToArrayOption(structOptions)
var err error
for i := 0; i < len(flds); i++ {
if flds[i].keyAsInt {
nameAsInt, numErr := strconv.Atoi(flds[i].name)
if numErr != nil {
err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")")
break
}
flds[i].nameAsInt = int64(nameAsInt)
}
flds[i].typInfo = getTypeInfo(flds[i].typ)
}
structType := &decodingStructType{fields: flds, err: err, toArray: toArray}
decodingStructTypeCache.Store(t, structType)
return structType
}
type encodingStructType struct {
fields fields
bytewiseFields fields
lengthFirstFields fields
err error
toArray bool
omitEmpty bool
hasAnonymousField bool
}
func (st *encodingStructType) getFields(em *encMode) fields {
if em.sort == SortNone {
return st.fields
}
if em.sort == SortLengthFirst {
return st.lengthFirstFields
}
return st.bytewiseFields
}
type bytewiseFieldSorter struct {
fields fields
}
func (x *bytewiseFieldSorter) Len() int {
return len(x.fields)
}
func (x *bytewiseFieldSorter) Swap(i, j int) {
x.fields[i], x.fields[j] = x.fields[j], x.fields[i]
}
func (x *bytewiseFieldSorter) Less(i, j int) bool {
return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0
}
type lengthFirstFieldSorter struct {
fields fields
}
func (x *lengthFirstFieldSorter) Len() int {
return len(x.fields)
}
func (x *lengthFirstFieldSorter) Swap(i, j int) {
x.fields[i], x.fields[j] = x.fields[j], x.fields[i]
}
func (x *lengthFirstFieldSorter) Less(i, j int) bool {
if len(x.fields[i].cborName) != len(x.fields[j].cborName) {
return len(x.fields[i].cborName) < len(x.fields[j].cborName)
}
return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0
}
func getEncodingStructType(t reflect.Type) *encodingStructType {
if v, _ := encodingStructTypeCache.Load(t); v != nil {
return v.(*encodingStructType)
}
flds, structOptions := getFields(t)
if hasToArrayOption(structOptions) {
return getEncodingStructToArrayType(t, flds)
}
var err error
var omitEmpty bool
var hasAnonymousField bool
var hasKeyAsInt bool
var hasKeyAsStr bool
e := getEncodeState()
for i := 0; i < len(flds); i++ {
// Get field's encodeFunc
flds[i].ef = getEncodeFunc(flds[i].typ)
if flds[i].ef == nil {
err = &UnsupportedTypeError{t}
break
}
// Encode field name
if flds[i].keyAsInt {
nameAsInt, numErr := strconv.Atoi(flds[i].name)
if numErr != nil {
err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")")
break
}
flds[i].nameAsInt = int64(nameAsInt)
if nameAsInt >= 0 {
encodeHead(e, byte(cborTypePositiveInt), uint64(nameAsInt))
} else {
n := nameAsInt*(-1) - 1
encodeHead(e, byte(cborTypeNegativeInt), uint64(n))
}
flds[i].cborName = make([]byte, e.Len())
copy(flds[i].cborName, e.Bytes())
e.Reset()
hasKeyAsInt = true
} else {
encodeHead(e, byte(cborTypeTextString), uint64(len(flds[i].name)))
flds[i].cborName = make([]byte, e.Len()+len(flds[i].name))
n := copy(flds[i].cborName, e.Bytes())
copy(flds[i].cborName[n:], flds[i].name)
e.Reset()
hasKeyAsStr = true
}
// Check if field is from embedded struct
if len(flds[i].idx) > 1 {
hasAnonymousField = true
}
// Check if field can be omitted when empty
if flds[i].omitEmpty {
omitEmpty = true
}
}
putEncodeState(e)
if err != nil {
structType := &encodingStructType{err: err}
encodingStructTypeCache.Store(t, structType)
return structType
}
// Sort fields by canonical order
bytewiseFields := make(fields, len(flds))
copy(bytewiseFields, flds)
sort.Sort(&bytewiseFieldSorter{bytewiseFields})
lengthFirstFields := bytewiseFields
if hasKeyAsInt && hasKeyAsStr {
lengthFirstFields = make(fields, len(flds))
copy(lengthFirstFields, flds)
sort.Sort(&lengthFirstFieldSorter{lengthFirstFields})
}
structType := &encodingStructType{
fields: flds,
bytewiseFields: bytewiseFields,
lengthFirstFields: lengthFirstFields,
omitEmpty: omitEmpty,
hasAnonymousField: hasAnonymousField,
}
encodingStructTypeCache.Store(t, structType)
return structType
}
func getEncodingStructToArrayType(t reflect.Type, flds fields) *encodingStructType {
var hasAnonymousField bool
for i := 0; i < len(flds); i++ {
// Get field's encodeFunc
flds[i].ef = getEncodeFunc(flds[i].typ)
if flds[i].ef == nil {
structType := &encodingStructType{err: &UnsupportedTypeError{t}}
encodingStructTypeCache.Store(t, structType)
return structType
}
// Check if field is from embedded struct
if len(flds[i].idx) > 1 {
hasAnonymousField = true
}
}
structType := &encodingStructType{
fields: flds,
toArray: true,
hasAnonymousField: hasAnonymousField,
}
encodingStructTypeCache.Store(t, structType)
return structType
}
func getEncodeFunc(t reflect.Type) encodeFunc {
if v, _ := encodeFuncCache.Load(t); v != nil {
return v.(encodeFunc)
}
f := getEncodeFuncInternal(t)
encodeFuncCache.Store(t, f)
return f
}
func getTypeInfo(t reflect.Type) *typeInfo {
if v, _ := typeInfoCache.Load(t); v != nil {
return v.(*typeInfo)
}
tInfo := newTypeInfo(t)
typeInfoCache.Store(t, tInfo)
return tInfo
}
func hasToArrayOption(tag string) bool {
s := ",toarray"
idx := strings.Index(tag, s)
return idx >= 0 && (len(tag) == idx+len(s) || tag[idx+len(s)] == ',')
}