Update
This commit is contained in:
1 parent
3530afb854
commit
6903fcff27
2146 files changed
+112256
-190703
No files matched your search
+153
-110
@@ -92,94 +92,126 @@ func newTypeInfo(t reflect.Type) *typeInfo {
|
||||
}
|
||||
|
||||
type decodingStructType struct {
|
||||
fields fields
|
||||
fieldIndicesByName map[string]int
|
||||
err error
|
||||
toArray bool
|
||||
fields decodingFields
|
||||
fieldIndicesByName map[string]int // Only populated if toArray is false
|
||||
fieldIndicesByIntKey map[int64]int // Only populated if toArray is false
|
||||
err error
|
||||
toArray bool
|
||||
}
|
||||
|
||||
// The stdlib errors.Join was introduced in Go 1.20, and we still support Go 1.17, so instead,
|
||||
// here's a very basic implementation of an aggregated error.
|
||||
type multierror []error
|
||||
|
||||
func (m multierror) Error() string {
|
||||
var sb strings.Builder
|
||||
for i, err := range m {
|
||||
sb.WriteString(err.Error())
|
||||
if i < len(m)-1 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func getDecodingStructType(t reflect.Type) *decodingStructType {
|
||||
func getDecodingStructType(t reflect.Type) (*decodingStructType, error) {
|
||||
if v, _ := decodingStructTypeCache.Load(t); v != nil {
|
||||
return v.(*decodingStructType)
|
||||
structType := v.(*decodingStructType)
|
||||
if structType.err != nil {
|
||||
return nil, structType.err
|
||||
}
|
||||
return structType, nil
|
||||
}
|
||||
|
||||
flds, structOptions := getFields(t)
|
||||
|
||||
toArray := hasToArrayOption(structOptions)
|
||||
|
||||
var errs []error
|
||||
for i := 0; i < len(flds); i++ {
|
||||
if flds[i].keyAsInt {
|
||||
nameAsInt, numErr := strconv.Atoi(flds[i].name)
|
||||
if numErr != nil {
|
||||
errs = append(errs, 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)
|
||||
if toArray {
|
||||
return getDecodingStructToArrayType(t, flds)
|
||||
}
|
||||
|
||||
fieldIndicesByName := make(map[string]int, len(flds))
|
||||
for i, fld := range flds {
|
||||
if _, ok := fieldIndicesByName[fld.name]; ok {
|
||||
errs = append(errs, fmt.Errorf("cbor: two or more fields of %v have the same name %q", t, fld.name))
|
||||
continue
|
||||
}
|
||||
fieldIndicesByName[fld.name] = i
|
||||
}
|
||||
var fieldIndicesByIntKey map[int64]int
|
||||
|
||||
var err error
|
||||
{
|
||||
var multi multierror
|
||||
for _, each := range errs {
|
||||
if each != nil {
|
||||
multi = append(multi, each)
|
||||
decFlds := make(decodingFields, len(flds))
|
||||
for i, f := range flds {
|
||||
// nameAsInt is set in getFields() except for fields with an unparsable tagged name.
|
||||
// Atoi() is called here to catch and save parsing errors.
|
||||
if f.keyAsInt && f.nameAsInt == 0 {
|
||||
if _, numErr := strconv.Atoi(f.name); numErr != nil {
|
||||
structType := &decodingStructType{
|
||||
err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"),
|
||||
}
|
||||
decodingStructTypeCache.Store(t, structType)
|
||||
return nil, structType.err
|
||||
}
|
||||
}
|
||||
if len(multi) == 1 {
|
||||
err = multi[0]
|
||||
} else if len(multi) > 1 {
|
||||
err = multi
|
||||
|
||||
if f.keyAsInt {
|
||||
if fieldIndicesByIntKey == nil {
|
||||
fieldIndicesByIntKey = make(map[int64]int, len(flds))
|
||||
}
|
||||
// The duplication check is only a safeguard, since getFields() already deduplicates fields.
|
||||
if _, ok := fieldIndicesByIntKey[f.nameAsInt]; ok {
|
||||
structType := &decodingStructType{
|
||||
err: fmt.Errorf("cbor: two or more fields of %v have the same keyasint value %d", t, f.nameAsInt),
|
||||
}
|
||||
decodingStructTypeCache.Store(t, structType)
|
||||
return nil, structType.err
|
||||
}
|
||||
fieldIndicesByIntKey[f.nameAsInt] = i
|
||||
} else {
|
||||
// The duplication check is only a safeguard, since getFields() already deduplicates fields.
|
||||
if _, ok := fieldIndicesByName[f.name]; ok {
|
||||
structType := &decodingStructType{
|
||||
err: fmt.Errorf("cbor: two or more fields of %v have the same name %q", t, f.name),
|
||||
}
|
||||
decodingStructTypeCache.Store(t, structType)
|
||||
return nil, structType.err
|
||||
}
|
||||
fieldIndicesByName[f.name] = i
|
||||
}
|
||||
|
||||
decFlds[i] = &decodingField{
|
||||
field: *f,
|
||||
typInfo: getTypeInfo(f.typ),
|
||||
}
|
||||
}
|
||||
|
||||
structType := &decodingStructType{
|
||||
fields: flds,
|
||||
fieldIndicesByName: fieldIndicesByName,
|
||||
err: err,
|
||||
toArray: toArray,
|
||||
fields: decFlds,
|
||||
fieldIndicesByName: fieldIndicesByName,
|
||||
fieldIndicesByIntKey: fieldIndicesByIntKey,
|
||||
}
|
||||
decodingStructTypeCache.Store(t, structType)
|
||||
return structType
|
||||
return structType, nil
|
||||
}
|
||||
|
||||
func getDecodingStructToArrayType(t reflect.Type, flds fields) (*decodingStructType, error) {
|
||||
decFlds := make(decodingFields, len(flds))
|
||||
for i, f := range flds {
|
||||
// nameAsInt is set in getFields() except for fields with an unparsable tagged name.
|
||||
// Atoi() is called here to catch and save parsing errors.
|
||||
if f.keyAsInt && f.nameAsInt == 0 {
|
||||
if _, numErr := strconv.Atoi(f.name); numErr != nil {
|
||||
structType := &decodingStructType{
|
||||
err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"),
|
||||
}
|
||||
decodingStructTypeCache.Store(t, structType)
|
||||
return nil, structType.err
|
||||
}
|
||||
}
|
||||
|
||||
decFlds[i] = &decodingField{
|
||||
field: *f,
|
||||
typInfo: getTypeInfo(f.typ),
|
||||
}
|
||||
}
|
||||
|
||||
structType := &decodingStructType{
|
||||
fields: decFlds,
|
||||
toArray: true,
|
||||
}
|
||||
decodingStructTypeCache.Store(t, structType)
|
||||
return structType, nil
|
||||
}
|
||||
|
||||
type encodingStructType struct {
|
||||
fields fields
|
||||
bytewiseFields fields
|
||||
lengthFirstFields fields
|
||||
omitEmptyFieldsIdx []int
|
||||
fields encodingFields
|
||||
bytewiseFields encodingFields // Only populated if toArray is false
|
||||
lengthFirstFields encodingFields // Only populated if toArray is false
|
||||
omitEmptyFieldsIdx []int // Only populated if toArray is false
|
||||
err error
|
||||
toArray bool
|
||||
}
|
||||
|
||||
func (st *encodingStructType) getFields(em *encMode) fields {
|
||||
func (st *encodingStructType) getFields(em *encMode) encodingFields {
|
||||
switch em.sort {
|
||||
case SortNone, SortFastShuffle:
|
||||
return st.fields
|
||||
@@ -191,7 +223,7 @@ func (st *encodingStructType) getFields(em *encMode) fields {
|
||||
}
|
||||
|
||||
type bytewiseFieldSorter struct {
|
||||
fields fields
|
||||
fields encodingFields
|
||||
}
|
||||
|
||||
func (x *bytewiseFieldSorter) Len() int {
|
||||
@@ -203,11 +235,11 @@ func (x *bytewiseFieldSorter) Swap(i, j int) {
|
||||
}
|
||||
|
||||
func (x *bytewiseFieldSorter) Less(i, j int) bool {
|
||||
return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) <= 0
|
||||
return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) < 0
|
||||
}
|
||||
|
||||
type lengthFirstFieldSorter struct {
|
||||
fields fields
|
||||
fields encodingFields
|
||||
}
|
||||
|
||||
func (x *lengthFirstFieldSorter) Len() int {
|
||||
@@ -222,13 +254,16 @@ 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
|
||||
return bytes.Compare(x.fields[i].cborName, x.fields[j].cborName) < 0
|
||||
}
|
||||
|
||||
func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
if v, _ := encodingStructTypeCache.Load(t); v != nil {
|
||||
structType := v.(*encodingStructType)
|
||||
return structType, structType.err
|
||||
if structType.err != nil {
|
||||
return nil, structType.err
|
||||
}
|
||||
return structType, nil
|
||||
}
|
||||
|
||||
flds, structOptions := getFields(t)
|
||||
@@ -237,111 +272,119 @@ func getEncodingStructType(t reflect.Type) (*encodingStructType, error) {
|
||||
return getEncodingStructToArrayType(t, flds)
|
||||
}
|
||||
|
||||
var err error
|
||||
var hasKeyAsInt bool
|
||||
var hasKeyAsStr bool
|
||||
var omitEmptyIdx []int
|
||||
|
||||
encFlds := make(encodingFields, len(flds))
|
||||
|
||||
e := getEncodeBuffer()
|
||||
for i := 0; i < len(flds); i++ {
|
||||
defer putEncodeBuffer(e)
|
||||
|
||||
for i, f := range flds {
|
||||
encFlds[i] = &encodingField{field: *f}
|
||||
ef := encFlds[i]
|
||||
|
||||
// Get field's encodeFunc
|
||||
flds[i].ef, flds[i].ief, flds[i].izf = getEncodeFunc(flds[i].typ)
|
||||
if flds[i].ef == nil {
|
||||
err = &UnsupportedTypeError{t}
|
||||
break
|
||||
ef.ef, ef.ief, ef.izf = getEncodeFunc(f.typ)
|
||||
if ef.ef == nil {
|
||||
structType := &encodingStructType{err: &UnsupportedTypeError{t}}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return nil, structType.err
|
||||
}
|
||||
|
||||
// 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
|
||||
if f.keyAsInt {
|
||||
if f.nameAsInt == 0 {
|
||||
// nameAsInt is set in getFields() except for fields with an unparsable tagged name.
|
||||
// Atoi() is called here to catch and save parsing errors.
|
||||
if _, numErr := strconv.Atoi(f.name); numErr != nil {
|
||||
structType := &encodingStructType{
|
||||
err: errors.New("cbor: failed to parse field name \"" + f.name + "\" to int (" + numErr.Error() + ")"),
|
||||
}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return nil, structType.err
|
||||
}
|
||||
}
|
||||
flds[i].nameAsInt = int64(nameAsInt)
|
||||
nameAsInt := f.nameAsInt
|
||||
if nameAsInt >= 0 {
|
||||
encodeHead(e, byte(cborTypePositiveInt), uint64(nameAsInt))
|
||||
encodeHead(e, byte(cborTypePositiveInt), uint64(nameAsInt)) //nolint:gosec
|
||||
} else {
|
||||
n := nameAsInt*(-1) - 1
|
||||
encodeHead(e, byte(cborTypeNegativeInt), uint64(n))
|
||||
encodeHead(e, byte(cborTypeNegativeInt), uint64(n)) //nolint:gosec
|
||||
}
|
||||
flds[i].cborName = make([]byte, e.Len())
|
||||
copy(flds[i].cborName, e.Bytes())
|
||||
ef.cborName = make([]byte, e.Len())
|
||||
copy(ef.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)
|
||||
encodeHead(e, byte(cborTypeTextString), uint64(len(f.name)))
|
||||
ef.cborName = make([]byte, e.Len()+len(f.name))
|
||||
n := copy(ef.cborName, e.Bytes())
|
||||
copy(ef.cborName[n:], f.name)
|
||||
e.Reset()
|
||||
|
||||
// If cborName contains a text string, then cborNameByteString contains a
|
||||
// string that has the byte string major type but is otherwise identical to
|
||||
// cborName.
|
||||
flds[i].cborNameByteString = make([]byte, len(flds[i].cborName))
|
||||
copy(flds[i].cborNameByteString, flds[i].cborName)
|
||||
ef.cborNameByteString = make([]byte, len(ef.cborName))
|
||||
copy(ef.cborNameByteString, ef.cborName)
|
||||
// Reset encoded CBOR type to byte string, preserving the "additional
|
||||
// information" bits:
|
||||
flds[i].cborNameByteString[0] = byte(cborTypeByteString) |
|
||||
getAdditionalInformation(flds[i].cborNameByteString[0])
|
||||
ef.cborNameByteString[0] = byte(cborTypeByteString) |
|
||||
getAdditionalInformation(ef.cborNameByteString[0])
|
||||
|
||||
hasKeyAsStr = true
|
||||
}
|
||||
|
||||
// Check if field can be omitted when empty
|
||||
if flds[i].omitEmpty {
|
||||
if f.omitEmpty {
|
||||
omitEmptyIdx = append(omitEmptyIdx, i)
|
||||
}
|
||||
}
|
||||
putEncodeBuffer(e)
|
||||
|
||||
if err != nil {
|
||||
structType := &encodingStructType{err: err}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
}
|
||||
|
||||
// Sort fields by canonical order
|
||||
bytewiseFields := make(fields, len(flds))
|
||||
copy(bytewiseFields, flds)
|
||||
bytewiseFields := make(encodingFields, len(encFlds))
|
||||
copy(bytewiseFields, encFlds)
|
||||
sort.Sort(&bytewiseFieldSorter{bytewiseFields})
|
||||
|
||||
lengthFirstFields := bytewiseFields
|
||||
if hasKeyAsInt && hasKeyAsStr {
|
||||
lengthFirstFields = make(fields, len(flds))
|
||||
copy(lengthFirstFields, flds)
|
||||
lengthFirstFields = make(encodingFields, len(encFlds))
|
||||
copy(lengthFirstFields, encFlds)
|
||||
sort.Sort(&lengthFirstFieldSorter{lengthFirstFields})
|
||||
}
|
||||
|
||||
structType := &encodingStructType{
|
||||
fields: flds,
|
||||
fields: encFlds,
|
||||
bytewiseFields: bytewiseFields,
|
||||
lengthFirstFields: lengthFirstFields,
|
||||
omitEmptyFieldsIdx: omitEmptyIdx,
|
||||
}
|
||||
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
return structType, nil
|
||||
}
|
||||
|
||||
func getEncodingStructToArrayType(t reflect.Type, flds fields) (*encodingStructType, error) {
|
||||
for i := 0; i < len(flds); i++ {
|
||||
// Get field's encodeFunc
|
||||
flds[i].ef, flds[i].ief, flds[i].izf = getEncodeFunc(flds[i].typ)
|
||||
if flds[i].ef == nil {
|
||||
encFlds := make(encodingFields, len(flds))
|
||||
for i, f := range flds {
|
||||
encFlds[i] = &encodingField{field: *f}
|
||||
encFlds[i].ef, encFlds[i].ief, encFlds[i].izf = getEncodeFunc(f.typ)
|
||||
if encFlds[i].ef == nil {
|
||||
structType := &encodingStructType{err: &UnsupportedTypeError{t}}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
return nil, structType.err
|
||||
}
|
||||
}
|
||||
|
||||
structType := &encodingStructType{
|
||||
fields: flds,
|
||||
fields: encFlds,
|
||||
toArray: true,
|
||||
}
|
||||
encodingStructTypeCache.Store(t, structType)
|
||||
return structType, structType.err
|
||||
return structType, nil
|
||||
}
|
||||
|
||||
func getEncodeFunc(t reflect.Type) (encodeFunc, isEmptyFunc, isZeroFunc) {
|
||||
|
||||
Reference in new issue
Block a user