This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+153 -110
View File
@@ -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) {